/mandos/release

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/release

« back to all changes in this revision

Viewing changes to plugins.d/mandos-client.c

Merge from release branch.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/*  -*- coding: utf-8 -*- */
2
2
/*
3
 
 * Mandos client - get and decrypt data from a Mandos server
 
3
 * Mandos-client - get and decrypt data from a Mandos server
4
4
 *
5
5
 * This program is partly derived from an example program for an Avahi
6
6
 * service browser, downloaded from
9
9
 * "browse_callback", and parts of "main".
10
10
 * 
11
11
 * Everything else is
12
 
 * Copyright © 2007-2008 Teddy Hogeborn & Björn Påhlsson
 
12
 * Copyright © 2008,2009 Teddy Hogeborn
 
13
 * Copyright © 2008,2009 Björn Påhlsson
13
14
 * 
14
15
 * This program is free software: you can redistribute it and/or
15
16
 * modify it under the terms of the GNU General Public License as
25
26
 * along with this program.  If not, see
26
27
 * <http://www.gnu.org/licenses/>.
27
28
 * 
28
 
 * Contact the authors at <https://www.fukt.bsnet.se/~belorn/> and
29
 
 * <https://www.fukt.bsnet.se/~teddy/>.
 
29
 * Contact the authors at <mandos@fukt.bsnet.se>.
30
30
 */
31
31
 
32
32
/* Needed by GPGME, specifically gpgme_data_seek() */
 
33
#ifndef _LARGEFILE_SOURCE
33
34
#define _LARGEFILE_SOURCE
 
35
#endif
 
36
#ifndef _FILE_OFFSET_BITS
34
37
#define _FILE_OFFSET_BITS 64
35
 
 
36
 
#include <stdio.h>
37
 
#include <assert.h>
38
 
#include <stdlib.h>
39
 
#include <time.h>
40
 
#include <net/if.h>             /* if_nametoindex */
41
 
 
 
38
#endif
 
39
 
 
40
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY(), asprintf() */
 
41
 
 
42
#include <stdio.h>              /* fprintf(), stderr, fwrite(),
 
43
                                   stdout, ferror(), remove() */
 
44
#include <stdint.h>             /* uint16_t, uint32_t */
 
45
#include <stddef.h>             /* NULL, size_t, ssize_t */
 
46
#include <stdlib.h>             /* free(), EXIT_SUCCESS, EXIT_FAILURE,
 
47
                                   srand(), strtof(), abort() */
 
48
#include <stdbool.h>            /* bool, false, true */
 
49
#include <string.h>             /* memset(), strcmp(), strlen(),
 
50
                                   strerror(), asprintf(), strcpy() */
 
51
#include <sys/ioctl.h>          /* ioctl */
 
52
#include <sys/types.h>          /* socket(), inet_pton(), sockaddr,
 
53
                                   sockaddr_in6, PF_INET6,
 
54
                                   SOCK_STREAM, uid_t, gid_t, open(),
 
55
                                   opendir(), DIR */
 
56
#include <sys/stat.h>           /* open() */
 
57
#include <sys/socket.h>         /* socket(), struct sockaddr_in6,
 
58
                                   inet_pton(), connect() */
 
59
#include <fcntl.h>              /* open() */
 
60
#include <dirent.h>             /* opendir(), struct dirent, readdir()
 
61
                                 */
 
62
#include <inttypes.h>           /* PRIu16, PRIdMAX, intmax_t,
 
63
                                   strtoimax() */
 
64
#include <assert.h>             /* assert() */
 
65
#include <errno.h>              /* perror(), errno */
 
66
#include <time.h>               /* nanosleep(), time() */
 
67
#include <net/if.h>             /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
 
68
                                   SIOCSIFFLAGS, if_indextoname(),
 
69
                                   if_nametoindex(), IF_NAMESIZE */
 
70
#include <netinet/in.h>         /* IN6_IS_ADDR_LINKLOCAL,
 
71
                                   INET_ADDRSTRLEN, INET6_ADDRSTRLEN
 
72
                                */
 
73
#include <unistd.h>             /* close(), SEEK_SET, off_t, write(),
 
74
                                   getuid(), getgid(), seteuid(),
 
75
                                   setgid(), pause() */
 
76
#include <arpa/inet.h>          /* inet_pton(), htons */
 
77
#include <iso646.h>             /* not, or, and */
 
78
#include <argp.h>               /* struct argp_option, error_t, struct
 
79
                                   argp_state, struct argp,
 
80
                                   argp_parse(), ARGP_KEY_ARG,
 
81
                                   ARGP_KEY_END, ARGP_ERR_UNKNOWN */
 
82
#include <signal.h>             /* sigemptyset(), sigaddset(),
 
83
                                   sigaction(), SIGTERM, sig_atomic_t,
 
84
                                   raise() */
 
85
#include <sysexits.h>           /* EX_OSERR, EX_USAGE */
 
86
 
 
87
#ifdef __linux__
 
88
#include <sys/klog.h>           /* klogctl() */
 
89
#endif  /* __linux__ */
 
90
 
 
91
/* Avahi */
 
92
/* All Avahi types, constants and functions
 
93
 Avahi*, avahi_*,
 
94
 AVAHI_* */
42
95
#include <avahi-core/core.h>
43
96
#include <avahi-core/lookup.h>
44
97
#include <avahi-core/log.h>
46
99
#include <avahi-common/malloc.h>
47
100
#include <avahi-common/error.h>
48
101
 
49
 
//mandos client part
50
 
#include <sys/types.h>          /* socket(), inet_pton() */
51
 
#include <sys/socket.h>         /* socket(), struct sockaddr_in6,
52
 
                                   struct in6_addr, inet_pton() */
53
 
#include <gnutls/gnutls.h>      /* All GnuTLS stuff */
54
 
#include <gnutls/openpgp.h>     /* GnuTLS with openpgp stuff */
55
 
 
56
 
#include <unistd.h>             /* close() */
57
 
#include <netinet/in.h>
58
 
#include <stdbool.h>            /* true */
59
 
#include <string.h>             /* memset */
60
 
#include <arpa/inet.h>          /* inet_pton() */
61
 
#include <iso646.h>             /* not */
62
 
 
63
 
// gpgme
64
 
#include <errno.h>              /* perror() */
65
 
#include <gpgme.h>
66
 
 
67
 
// getopt long
68
 
#include <getopt.h>
 
102
/* GnuTLS */
 
103
#include <gnutls/gnutls.h>      /* All GnuTLS types, constants and
 
104
                                   functions:
 
105
                                   gnutls_*
 
106
                                   init_gnutls_session(),
 
107
                                   GNUTLS_* */
 
108
#include <gnutls/openpgp.h>
 
109
                          /* gnutls_certificate_set_openpgp_key_file(),
 
110
                                   GNUTLS_OPENPGP_FMT_BASE64 */
 
111
 
 
112
/* GPGME */
 
113
#include <gpgme.h>              /* All GPGME types, constants and
 
114
                                   functions:
 
115
                                   gpgme_*
 
116
                                   GPGME_PROTOCOL_OpenPGP,
 
117
                                   GPG_ERR_NO_* */
69
118
 
70
119
#define BUFFER_SIZE 256
71
 
#define DH_BITS 1024
72
120
 
73
 
const char *certdir = "/conf/conf.d/cryptkeyreq/";
74
 
const char *certfile = "openpgp-client.txt";
75
 
const char *certkey = "openpgp-client-key.txt";
 
121
#define PATHDIR "/conf/conf.d/mandos"
 
122
#define SECKEY "seckey.txt"
 
123
#define PUBKEY "pubkey.txt"
76
124
 
77
125
bool debug = false;
 
126
static const char mandos_protocol_version[] = "1";
 
127
const char *argp_program_version = "mandos-client " VERSION;
 
128
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
78
129
 
 
130
/* Used for passing in values through the Avahi callback functions */
79
131
typedef struct {
80
 
  gnutls_session_t session;
 
132
  AvahiSimplePoll *simple_poll;
 
133
  AvahiServer *server;
81
134
  gnutls_certificate_credentials_t cred;
 
135
  unsigned int dh_bits;
82
136
  gnutls_dh_params_t dh_params;
83
 
} encrypted_session;
84
 
 
85
 
 
86
 
ssize_t pgp_packet_decrypt (char *packet, size_t packet_size,
87
 
                            char **new_packet, const char *homedir){
88
 
  gpgme_data_t dh_crypto, dh_plain;
 
137
  const char *priority;
89
138
  gpgme_ctx_t ctx;
 
139
} mandos_context;
 
140
 
 
141
/* global context so signal handler can reach it*/
 
142
mandos_context mc = { .simple_poll = NULL, .server = NULL,
 
143
                      .dh_bits = 1024, .priority = "SECURE256"
 
144
                      ":!CTYPE-X.509:+CTYPE-OPENPGP" };
 
145
 
 
146
sig_atomic_t quit_now = 0;
 
147
int signal_received = 0;
 
148
 
 
149
/*
 
150
 * Make additional room in "buffer" for at least BUFFER_SIZE more
 
151
 * bytes. "buffer_capacity" is how much is currently allocated,
 
152
 * "buffer_length" is how much is already used.
 
153
 */
 
154
size_t incbuffer(char **buffer, size_t buffer_length,
 
155
                  size_t buffer_capacity){
 
156
  if(buffer_length + BUFFER_SIZE > buffer_capacity){
 
157
    *buffer = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
 
158
    if(buffer == NULL){
 
159
      return 0;
 
160
    }
 
161
    buffer_capacity += BUFFER_SIZE;
 
162
  }
 
163
  return buffer_capacity;
 
164
}
 
165
 
 
166
/* 
 
167
 * Initialize GPGME.
 
168
 */
 
169
static bool init_gpgme(const char *seckey,
 
170
                       const char *pubkey, const char *tempdir){
90
171
  gpgme_error_t rc;
91
 
  ssize_t ret;
92
 
  ssize_t new_packet_capacity = 0;
93
 
  ssize_t new_packet_length = 0;
94
172
  gpgme_engine_info_t engine_info;
95
 
 
96
 
  if (debug){
97
 
    fprintf(stderr, "Trying to decrypt OpenPGP packet\n");
 
173
  
 
174
  
 
175
  /*
 
176
   * Helper function to insert pub and seckey to the engine keyring.
 
177
   */
 
178
  bool import_key(const char *filename){
 
179
    int ret;
 
180
    int fd;
 
181
    gpgme_data_t pgp_data;
 
182
    
 
183
    fd = (int)TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
 
184
    if(fd == -1){
 
185
      perror("open");
 
186
      return false;
 
187
    }
 
188
    
 
189
    rc = gpgme_data_new_from_fd(&pgp_data, fd);
 
190
    if(rc != GPG_ERR_NO_ERROR){
 
191
      fprintf(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
 
192
              gpgme_strsource(rc), gpgme_strerror(rc));
 
193
      return false;
 
194
    }
 
195
    
 
196
    rc = gpgme_op_import(mc.ctx, pgp_data);
 
197
    if(rc != GPG_ERR_NO_ERROR){
 
198
      fprintf(stderr, "bad gpgme_op_import: %s: %s\n",
 
199
              gpgme_strsource(rc), gpgme_strerror(rc));
 
200
      return false;
 
201
    }
 
202
    
 
203
    ret = (int)TEMP_FAILURE_RETRY(close(fd));
 
204
    if(ret == -1){
 
205
      perror("close");
 
206
    }
 
207
    gpgme_data_release(pgp_data);
 
208
    return true;
 
209
  }
 
210
  
 
211
  if(debug){
 
212
    fprintf(stderr, "Initializing GPGME\n");
98
213
  }
99
214
  
100
215
  /* Init GPGME */
101
216
  gpgme_check_version(NULL);
102
217
  rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
103
 
  if (rc != GPG_ERR_NO_ERROR){
 
218
  if(rc != GPG_ERR_NO_ERROR){
104
219
    fprintf(stderr, "bad gpgme_engine_check_version: %s: %s\n",
105
220
            gpgme_strsource(rc), gpgme_strerror(rc));
106
 
    return -1;
 
221
    return false;
107
222
  }
108
223
  
109
 
  /* Set GPGME home directory */
110
 
  rc = gpgme_get_engine_info (&engine_info);
111
 
  if (rc != GPG_ERR_NO_ERROR){
 
224
    /* Set GPGME home directory for the OpenPGP engine only */
 
225
  rc = gpgme_get_engine_info(&engine_info);
 
226
  if(rc != GPG_ERR_NO_ERROR){
112
227
    fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
113
228
            gpgme_strsource(rc), gpgme_strerror(rc));
114
 
    return -1;
 
229
    return false;
115
230
  }
116
231
  while(engine_info != NULL){
117
232
    if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
118
233
      gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
119
 
                            engine_info->file_name, homedir);
 
234
                            engine_info->file_name, tempdir);
120
235
      break;
121
236
    }
122
237
    engine_info = engine_info->next;
123
238
  }
124
239
  if(engine_info == NULL){
125
 
    fprintf(stderr, "Could not set home dir to %s\n", homedir);
126
 
    return -1;
127
 
  }
128
 
  
129
 
  /* Create new GPGME data buffer from packet buffer */
130
 
  rc = gpgme_data_new_from_mem(&dh_crypto, packet, packet_size, 0);
131
 
  if (rc != GPG_ERR_NO_ERROR){
 
240
    fprintf(stderr, "Could not set GPGME home dir to %s\n", tempdir);
 
241
    return false;
 
242
  }
 
243
  
 
244
  /* Create new GPGME "context" */
 
245
  rc = gpgme_new(&(mc.ctx));
 
246
  if(rc != GPG_ERR_NO_ERROR){
 
247
    fprintf(stderr, "bad gpgme_new: %s: %s\n",
 
248
            gpgme_strsource(rc), gpgme_strerror(rc));
 
249
    return false;
 
250
  }
 
251
  
 
252
  if(not import_key(pubkey) or not import_key(seckey)){
 
253
    return false;
 
254
  }
 
255
  
 
256
  return true;
 
257
}
 
258
 
 
259
/* 
 
260
 * Decrypt OpenPGP data.
 
261
 * Returns -1 on error
 
262
 */
 
263
static ssize_t pgp_packet_decrypt(const char *cryptotext,
 
264
                                  size_t crypto_size,
 
265
                                  char **plaintext){
 
266
  gpgme_data_t dh_crypto, dh_plain;
 
267
  gpgme_error_t rc;
 
268
  ssize_t ret;
 
269
  size_t plaintext_capacity = 0;
 
270
  ssize_t plaintext_length = 0;
 
271
  
 
272
  if(debug){
 
273
    fprintf(stderr, "Trying to decrypt OpenPGP data\n");
 
274
  }
 
275
  
 
276
  /* Create new GPGME data buffer from memory cryptotext */
 
277
  rc = gpgme_data_new_from_mem(&dh_crypto, cryptotext, crypto_size,
 
278
                               0);
 
279
  if(rc != GPG_ERR_NO_ERROR){
132
280
    fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
133
281
            gpgme_strsource(rc), gpgme_strerror(rc));
134
282
    return -1;
136
284
  
137
285
  /* Create new empty GPGME data buffer for the plaintext */
138
286
  rc = gpgme_data_new(&dh_plain);
139
 
  if (rc != GPG_ERR_NO_ERROR){
 
287
  if(rc != GPG_ERR_NO_ERROR){
140
288
    fprintf(stderr, "bad gpgme_data_new: %s: %s\n",
141
289
            gpgme_strsource(rc), gpgme_strerror(rc));
142
 
    return -1;
143
 
  }
144
 
  
145
 
  /* Create new GPGME "context" */
146
 
  rc = gpgme_new(&ctx);
147
 
  if (rc != GPG_ERR_NO_ERROR){
148
 
    fprintf(stderr, "bad gpgme_new: %s: %s\n",
149
 
            gpgme_strsource(rc), gpgme_strerror(rc));
150
 
    return -1;
151
 
  }
152
 
  
153
 
  /* Decrypt data from the FILE pointer to the plaintext data
154
 
     buffer */
155
 
  rc = gpgme_op_decrypt(ctx, dh_crypto, dh_plain);
156
 
  if (rc != GPG_ERR_NO_ERROR){
 
290
    gpgme_data_release(dh_crypto);
 
291
    return -1;
 
292
  }
 
293
  
 
294
  /* Decrypt data from the cryptotext data buffer to the plaintext
 
295
     data buffer */
 
296
  rc = gpgme_op_decrypt(mc.ctx, dh_crypto, dh_plain);
 
297
  if(rc != GPG_ERR_NO_ERROR){
157
298
    fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
158
299
            gpgme_strsource(rc), gpgme_strerror(rc));
159
 
    return -1;
160
 
  }
161
 
 
162
 
  if(debug){
163
 
    fprintf(stderr, "Decryption of OpenPGP packet succeeded\n");
164
 
  }
165
 
 
166
 
  if (debug){
167
 
    gpgme_decrypt_result_t result;
168
 
    result = gpgme_op_decrypt_result(ctx);
169
 
    if (result == NULL){
170
 
      fprintf(stderr, "gpgme_op_decrypt_result failed\n");
171
 
    } else {
172
 
      fprintf(stderr, "Unsupported algorithm: %s\n",
173
 
              result->unsupported_algorithm);
174
 
      fprintf(stderr, "Wrong key usage: %d\n",
175
 
              result->wrong_key_usage);
176
 
      if(result->file_name != NULL){
177
 
        fprintf(stderr, "File name: %s\n", result->file_name);
178
 
      }
179
 
      gpgme_recipient_t recipient;
180
 
      recipient = result->recipients;
181
 
      if(recipient){
 
300
    plaintext_length = -1;
 
301
    if(debug){
 
302
      gpgme_decrypt_result_t result;
 
303
      result = gpgme_op_decrypt_result(mc.ctx);
 
304
      if(result == NULL){
 
305
        fprintf(stderr, "gpgme_op_decrypt_result failed\n");
 
306
      } else {
 
307
        fprintf(stderr, "Unsupported algorithm: %s\n",
 
308
                result->unsupported_algorithm);
 
309
        fprintf(stderr, "Wrong key usage: %u\n",
 
310
                result->wrong_key_usage);
 
311
        if(result->file_name != NULL){
 
312
          fprintf(stderr, "File name: %s\n", result->file_name);
 
313
        }
 
314
        gpgme_recipient_t recipient;
 
315
        recipient = result->recipients;
182
316
        while(recipient != NULL){
183
317
          fprintf(stderr, "Public key algorithm: %s\n",
184
318
                  gpgme_pubkey_algo_name(recipient->pubkey_algo));
190
324
        }
191
325
      }
192
326
    }
 
327
    goto decrypt_end;
193
328
  }
194
329
  
195
 
  /* Delete the GPGME FILE pointer cryptotext data buffer */
196
 
  gpgme_data_release(dh_crypto);
 
330
  if(debug){
 
331
    fprintf(stderr, "Decryption of OpenPGP data succeeded\n");
 
332
  }
197
333
  
198
334
  /* Seek back to the beginning of the GPGME plaintext data buffer */
199
 
  if (gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET) == -1){
200
 
    perror("pgpme_data_seek");
 
335
  if(gpgme_data_seek(dh_plain, (off_t)0, SEEK_SET) == -1){
 
336
    perror("gpgme_data_seek");
 
337
    plaintext_length = -1;
 
338
    goto decrypt_end;
201
339
  }
202
340
  
203
 
  *new_packet = 0;
 
341
  *plaintext = NULL;
204
342
  while(true){
205
 
    if (new_packet_length + BUFFER_SIZE > new_packet_capacity){
206
 
      *new_packet = realloc(*new_packet,
207
 
                            (unsigned int)new_packet_capacity
208
 
                            + BUFFER_SIZE);
209
 
      if (*new_packet == NULL){
210
 
        perror("realloc");
211
 
        return -1;
212
 
      }
213
 
      new_packet_capacity += BUFFER_SIZE;
 
343
    plaintext_capacity = incbuffer(plaintext,
 
344
                                      (size_t)plaintext_length,
 
345
                                      plaintext_capacity);
 
346
    if(plaintext_capacity == 0){
 
347
        perror("incbuffer");
 
348
        plaintext_length = -1;
 
349
        goto decrypt_end;
214
350
    }
215
351
    
216
 
    ret = gpgme_data_read(dh_plain, *new_packet + new_packet_length,
 
352
    ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
217
353
                          BUFFER_SIZE);
218
354
    /* Print the data, if any */
219
 
    if (ret == 0){
 
355
    if(ret == 0){
 
356
      /* EOF */
220
357
      break;
221
358
    }
222
359
    if(ret < 0){
223
360
      perror("gpgme_data_read");
224
 
      return -1;
225
 
    }
226
 
    new_packet_length += ret;
227
 
  }
228
 
 
229
 
  /* FIXME: check characters before printing to screen so to not print
230
 
     terminal control characters */
231
 
  /*   if(debug){ */
232
 
  /*     fprintf(stderr, "decrypted password is: "); */
233
 
  /*     fwrite(*new_packet, 1, new_packet_length, stderr); */
234
 
  /*     fprintf(stderr, "\n"); */
235
 
  /*   } */
 
361
      plaintext_length = -1;
 
362
      goto decrypt_end;
 
363
    }
 
364
    plaintext_length += ret;
 
365
  }
 
366
  
 
367
  if(debug){
 
368
    fprintf(stderr, "Decrypted password is: ");
 
369
    for(ssize_t i = 0; i < plaintext_length; i++){
 
370
      fprintf(stderr, "%02hhX ", (*plaintext)[i]);
 
371
    }
 
372
    fprintf(stderr, "\n");
 
373
  }
 
374
  
 
375
 decrypt_end:
 
376
  
 
377
  /* Delete the GPGME cryptotext data buffer */
 
378
  gpgme_data_release(dh_crypto);
236
379
  
237
380
  /* Delete the GPGME plaintext data buffer */
238
381
  gpgme_data_release(dh_plain);
239
 
  return new_packet_length;
 
382
  return plaintext_length;
240
383
}
241
384
 
242
 
static const char * safer_gnutls_strerror (int value) {
243
 
  const char *ret = gnutls_strerror (value);
244
 
  if (ret == NULL)
 
385
static const char * safer_gnutls_strerror(int value){
 
386
  const char *ret = gnutls_strerror(value); /* Spurious warning from
 
387
                                               -Wunreachable-code */
 
388
  if(ret == NULL)
245
389
    ret = "(unknown)";
246
390
  return ret;
247
391
}
248
392
 
249
 
void debuggnutls(__attribute__((unused)) int level,
250
 
                 const char* string){
251
 
  fprintf(stderr, "%s", string);
 
393
/* GnuTLS log function callback */
 
394
static void debuggnutls(__attribute__((unused)) int level,
 
395
                        const char* string){
 
396
  fprintf(stderr, "GnuTLS: %s", string);
252
397
}
253
398
 
254
 
int initgnutls(encrypted_session *es){
255
 
  const char *err;
 
399
static int init_gnutls_global(const char *pubkeyfilename,
 
400
                              const char *seckeyfilename){
256
401
  int ret;
257
402
  
258
403
  if(debug){
259
404
    fprintf(stderr, "Initializing GnuTLS\n");
260
405
  }
261
 
 
262
 
  if ((ret = gnutls_global_init ())
263
 
      != GNUTLS_E_SUCCESS) {
264
 
    fprintf (stderr, "global_init: %s\n", safer_gnutls_strerror(ret));
 
406
  
 
407
  ret = gnutls_global_init();
 
408
  if(ret != GNUTLS_E_SUCCESS){
 
409
    fprintf(stderr, "GnuTLS global_init: %s\n",
 
410
            safer_gnutls_strerror(ret));
265
411
    return -1;
266
412
  }
267
 
 
268
 
  if (debug){
 
413
  
 
414
  if(debug){
 
415
    /* "Use a log level over 10 to enable all debugging options."
 
416
     * - GnuTLS manual
 
417
     */
269
418
    gnutls_global_set_log_level(11);
270
419
    gnutls_global_set_log_function(debuggnutls);
271
420
  }
272
421
  
273
 
  /* openpgp credentials */
274
 
  if ((ret = gnutls_certificate_allocate_credentials (&es->cred))
275
 
      != GNUTLS_E_SUCCESS) {
276
 
    fprintf (stderr, "memory error: %s\n",
277
 
             safer_gnutls_strerror(ret));
 
422
  /* OpenPGP credentials */
 
423
  gnutls_certificate_allocate_credentials(&mc.cred);
 
424
  if(ret != GNUTLS_E_SUCCESS){
 
425
    fprintf(stderr, "GnuTLS memory error: %s\n", /* Spurious warning
 
426
                                                    from
 
427
                                                    -Wunreachable-code
 
428
                                                 */
 
429
            safer_gnutls_strerror(ret));
 
430
    gnutls_global_deinit();
278
431
    return -1;
279
432
  }
280
433
  
281
434
  if(debug){
282
 
    fprintf(stderr, "Attempting to use OpenPGP certificate %s"
283
 
            " and keyfile %s as GnuTLS credentials\n", certfile,
284
 
            certkey);
 
435
    fprintf(stderr, "Attempting to use OpenPGP public key %s and"
 
436
            " secret key %s as GnuTLS credentials\n", pubkeyfilename,
 
437
            seckeyfilename);
285
438
  }
286
439
  
287
440
  ret = gnutls_certificate_set_openpgp_key_file
288
 
    (es->cred, certfile, certkey, GNUTLS_OPENPGP_FMT_BASE64);
289
 
  if (ret != GNUTLS_E_SUCCESS) {
290
 
    fprintf
291
 
      (stderr, "Error[%d] while reading the OpenPGP key pair ('%s',"
292
 
       " '%s')\n",
293
 
       ret, certfile, certkey);
294
 
    fprintf(stdout, "The Error is: %s\n",
295
 
            safer_gnutls_strerror(ret));
296
 
    return -1;
297
 
  }
298
 
  
299
 
  //GnuTLS server initialization
300
 
  if ((ret = gnutls_dh_params_init (&es->dh_params))
301
 
      != GNUTLS_E_SUCCESS) {
302
 
    fprintf (stderr, "Error in dh parameter initialization: %s\n",
303
 
             safer_gnutls_strerror(ret));
304
 
    return -1;
305
 
  }
306
 
  
307
 
  if ((ret = gnutls_dh_params_generate2 (es->dh_params, DH_BITS))
308
 
      != GNUTLS_E_SUCCESS) {
309
 
    fprintf (stderr, "Error in prime generation: %s\n",
310
 
             safer_gnutls_strerror(ret));
311
 
    return -1;
312
 
  }
313
 
  
314
 
  gnutls_certificate_set_dh_params (es->cred, es->dh_params);
315
 
  
316
 
  // GnuTLS session creation
317
 
  if ((ret = gnutls_init (&es->session, GNUTLS_SERVER))
318
 
      != GNUTLS_E_SUCCESS){
 
441
    (mc.cred, pubkeyfilename, seckeyfilename,
 
442
     GNUTLS_OPENPGP_FMT_BASE64);
 
443
  if(ret != GNUTLS_E_SUCCESS){
 
444
    fprintf(stderr,
 
445
            "Error[%d] while reading the OpenPGP key pair ('%s',"
 
446
            " '%s')\n", ret, pubkeyfilename, seckeyfilename);
 
447
    fprintf(stderr, "The GnuTLS error is: %s\n",
 
448
            safer_gnutls_strerror(ret));
 
449
    goto globalfail;
 
450
  }
 
451
  
 
452
  /* GnuTLS server initialization */
 
453
  ret = gnutls_dh_params_init(&mc.dh_params);
 
454
  if(ret != GNUTLS_E_SUCCESS){
 
455
    fprintf(stderr, "Error in GnuTLS DH parameter initialization:"
 
456
            " %s\n", safer_gnutls_strerror(ret));
 
457
    goto globalfail;
 
458
  }
 
459
  ret = gnutls_dh_params_generate2(mc.dh_params, mc.dh_bits);
 
460
  if(ret != GNUTLS_E_SUCCESS){
 
461
    fprintf(stderr, "Error in GnuTLS prime generation: %s\n",
 
462
            safer_gnutls_strerror(ret));
 
463
    goto globalfail;
 
464
  }
 
465
  
 
466
  gnutls_certificate_set_dh_params(mc.cred, mc.dh_params);
 
467
  
 
468
  return 0;
 
469
  
 
470
 globalfail:
 
471
  
 
472
  gnutls_certificate_free_credentials(mc.cred);
 
473
  gnutls_global_deinit();
 
474
  gnutls_dh_params_deinit(mc.dh_params);
 
475
  return -1;
 
476
}
 
477
 
 
478
static int init_gnutls_session(gnutls_session_t *session){
 
479
  int ret;
 
480
  /* GnuTLS session creation */
 
481
  do {
 
482
    ret = gnutls_init(session, GNUTLS_SERVER);
 
483
    if(quit_now){
 
484
      return -1;
 
485
    }
 
486
  } while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
 
487
  if(ret != GNUTLS_E_SUCCESS){
319
488
    fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
320
489
            safer_gnutls_strerror(ret));
321
490
  }
322
491
  
323
 
  if ((ret = gnutls_priority_set_direct (es->session, "NORMAL", &err))
324
 
      != GNUTLS_E_SUCCESS) {
325
 
    fprintf(stderr, "Syntax error at: %s\n", err);
326
 
    fprintf(stderr, "GnuTLS error: %s\n",
327
 
            safer_gnutls_strerror(ret));
328
 
    return -1;
 
492
  {
 
493
    const char *err;
 
494
    do {
 
495
      ret = gnutls_priority_set_direct(*session, mc.priority, &err);
 
496
      if(quit_now){
 
497
        gnutls_deinit(*session);
 
498
        return -1;
 
499
      }
 
500
    } while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
 
501
    if(ret != GNUTLS_E_SUCCESS){
 
502
      fprintf(stderr, "Syntax error at: %s\n", err);
 
503
      fprintf(stderr, "GnuTLS error: %s\n",
 
504
              safer_gnutls_strerror(ret));
 
505
      gnutls_deinit(*session);
 
506
      return -1;
 
507
    }
329
508
  }
330
509
  
331
 
  if ((ret = gnutls_credentials_set
332
 
       (es->session, GNUTLS_CRD_CERTIFICATE, es->cred))
333
 
      != GNUTLS_E_SUCCESS) {
334
 
    fprintf(stderr, "Error setting a credentials set: %s\n",
 
510
  do {
 
511
    ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
 
512
                                 mc.cred);
 
513
    if(quit_now){
 
514
      gnutls_deinit(*session);
 
515
      return -1;
 
516
    }
 
517
  } while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
 
518
  if(ret != GNUTLS_E_SUCCESS){
 
519
    fprintf(stderr, "Error setting GnuTLS credentials: %s\n",
335
520
            safer_gnutls_strerror(ret));
 
521
    gnutls_deinit(*session);
336
522
    return -1;
337
523
  }
338
524
  
339
525
  /* ignore client certificate if any. */
340
 
  gnutls_certificate_server_set_request (es->session,
341
 
                                         GNUTLS_CERT_IGNORE);
 
526
  gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE);
342
527
  
343
 
  gnutls_dh_set_prime_bits (es->session, DH_BITS);
 
528
  gnutls_dh_set_prime_bits(*session, mc.dh_bits);
344
529
  
345
530
  return 0;
346
531
}
347
532
 
348
 
void empty_log(__attribute__((unused)) AvahiLogLevel level,
349
 
               __attribute__((unused)) const char *txt){}
 
533
/* Avahi log function callback */
 
534
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
 
535
                      __attribute__((unused)) const char *txt){}
350
536
 
351
 
int start_mandos_communication(const char *ip, uint16_t port,
352
 
                               AvahiIfIndex if_index){
353
 
  int ret, tcp_sd;
354
 
  struct sockaddr_in6 to;
355
 
  encrypted_session es;
 
537
/* Called when a Mandos server is found */
 
538
static int start_mandos_communication(const char *ip, uint16_t port,
 
539
                                      AvahiIfIndex if_index,
 
540
                                      int af){
 
541
  int ret, tcp_sd = -1;
 
542
  ssize_t sret;
 
543
  union {
 
544
    struct sockaddr_in in;
 
545
    struct sockaddr_in6 in6;
 
546
  } to;
356
547
  char *buffer = NULL;
357
 
  char *decrypted_buffer;
 
548
  char *decrypted_buffer = NULL;
358
549
  size_t buffer_length = 0;
359
550
  size_t buffer_capacity = 0;
360
 
  ssize_t decrypted_buffer_size;
361
 
  size_t written = 0;
362
 
  int retval = 0;
363
 
  char interface[IF_NAMESIZE];
 
551
  size_t written;
 
552
  int retval = -1;
 
553
  gnutls_session_t session;
 
554
  int pf;                       /* Protocol family */
 
555
  
 
556
  if(quit_now){
 
557
    return -1;
 
558
  }
 
559
  
 
560
  switch(af){
 
561
  case AF_INET6:
 
562
    pf = PF_INET6;
 
563
    break;
 
564
  case AF_INET:
 
565
    pf = PF_INET;
 
566
    break;
 
567
  default:
 
568
    fprintf(stderr, "Bad address family: %d\n", af);
 
569
    return -1;
 
570
  }
 
571
  
 
572
  ret = init_gnutls_session(&session);
 
573
  if(ret != 0){
 
574
    return -1;
 
575
  }
364
576
  
365
577
  if(debug){
366
 
    fprintf(stderr, "Setting up a tcp connection to %s, port %d\n",
367
 
            ip, port);
 
578
    fprintf(stderr, "Setting up a TCP connection to %s, port %" PRIu16
 
579
            "\n", ip, port);
368
580
  }
369
581
  
370
 
  tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
371
 
  if(tcp_sd < 0) {
 
582
  tcp_sd = socket(pf, SOCK_STREAM, 0);
 
583
  if(tcp_sd < 0){
372
584
    perror("socket");
373
 
    return -1;
374
 
  }
375
 
  
376
 
  if(if_indextoname((unsigned int)if_index, interface) == NULL){
377
 
    if(debug){
378
 
      perror("if_indextoname");
379
 
    }
380
 
    return -1;
381
 
  }
382
 
  
383
 
  if(debug){
384
 
    fprintf(stderr, "Binding to interface %s\n", interface);
385
 
  }
386
 
  
387
 
  memset(&to,0,sizeof(to));     /* Spurious warning */
388
 
  to.sin6_family = AF_INET6;
389
 
  ret = inet_pton(AF_INET6, ip, &to.sin6_addr);
390
 
  if (ret < 0 ){
 
585
    goto mandos_end;
 
586
  }
 
587
  
 
588
  if(quit_now){
 
589
    goto mandos_end;
 
590
  }
 
591
  
 
592
  memset(&to, 0, sizeof(to));
 
593
  if(af == AF_INET6){
 
594
    to.in6.sin6_family = (sa_family_t)af;
 
595
    ret = inet_pton(af, ip, &to.in6.sin6_addr);
 
596
  } else {                      /* IPv4 */
 
597
    to.in.sin_family = (sa_family_t)af;
 
598
    ret = inet_pton(af, ip, &to.in.sin_addr);
 
599
  }
 
600
  if(ret < 0 ){
391
601
    perror("inet_pton");
392
 
    return -1;
393
 
  }  
 
602
    goto mandos_end;
 
603
  }
394
604
  if(ret == 0){
395
605
    fprintf(stderr, "Bad address: %s\n", ip);
396
 
    return -1;
397
 
  }
398
 
  to.sin6_port = htons(port);   /* Spurious warning */
 
606
    goto mandos_end;
 
607
  }
 
608
  if(af == AF_INET6){
 
609
    to.in6.sin6_port = htons(port); /* Spurious warnings from
 
610
                                       -Wconversion and
 
611
                                       -Wunreachable-code */
 
612
    
 
613
    if(IN6_IS_ADDR_LINKLOCAL /* Spurious warnings from */
 
614
       (&to.in6.sin6_addr)){ /* -Wstrict-aliasing=2 or lower and
 
615
                              -Wunreachable-code*/
 
616
      if(if_index == AVAHI_IF_UNSPEC){
 
617
        fprintf(stderr, "An IPv6 link-local address is incomplete"
 
618
                " without a network interface\n");
 
619
        goto mandos_end;
 
620
      }
 
621
      /* Set the network interface number as scope */
 
622
      to.in6.sin6_scope_id = (uint32_t)if_index;
 
623
    }
 
624
  } else {
 
625
    to.in.sin_port = htons(port); /* Spurious warnings from
 
626
                                     -Wconversion and
 
627
                                     -Wunreachable-code */
 
628
  }
399
629
  
400
 
  to.sin6_scope_id = (uint32_t)if_index;
 
630
  if(quit_now){
 
631
    goto mandos_end;
 
632
  }
401
633
  
402
634
  if(debug){
403
 
    fprintf(stderr, "Connection to: %s, port %d\n", ip, port);
404
 
/*     char addrstr[INET6_ADDRSTRLEN]; */
405
 
/*     if(inet_ntop(to.sin6_family, &(to.sin6_addr), addrstr, */
406
 
/*               sizeof(addrstr)) == NULL){ */
407
 
/*       perror("inet_ntop"); */
408
 
/*     } else { */
409
 
/*       fprintf(stderr, "Really connecting to: %s, port %d\n", */
410
 
/*            addrstr, ntohs(to.sin6_port)); */
411
 
/*     } */
412
 
  }
413
 
  
414
 
  ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
415
 
  if (ret < 0){
 
635
    if(af == AF_INET6 and if_index != AVAHI_IF_UNSPEC){
 
636
      char interface[IF_NAMESIZE];
 
637
      if(if_indextoname((unsigned int)if_index, interface) == NULL){
 
638
        perror("if_indextoname");
 
639
      } else {
 
640
        fprintf(stderr, "Connection to: %s%%%s, port %" PRIu16 "\n",
 
641
                ip, interface, port);
 
642
      }
 
643
    } else {
 
644
      fprintf(stderr, "Connection to: %s, port %" PRIu16 "\n", ip,
 
645
              port);
 
646
    }
 
647
    char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ?
 
648
                 INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = "";
 
649
    const char *pcret;
 
650
    if(af == AF_INET6){
 
651
      pcret = inet_ntop(af, &(to.in6.sin6_addr), addrstr,
 
652
                        sizeof(addrstr));
 
653
    } else {
 
654
      pcret = inet_ntop(af, &(to.in.sin_addr), addrstr,
 
655
                        sizeof(addrstr));
 
656
    }
 
657
    if(pcret == NULL){
 
658
      perror("inet_ntop");
 
659
    } else {
 
660
      if(strcmp(addrstr, ip) != 0){
 
661
        fprintf(stderr, "Canonical address form: %s\n", addrstr);
 
662
      }
 
663
    }
 
664
  }
 
665
  
 
666
  if(quit_now){
 
667
    goto mandos_end;
 
668
  }
 
669
  
 
670
  if(af == AF_INET6){
 
671
    ret = connect(tcp_sd, &to.in6, sizeof(to));
 
672
  } else {
 
673
    ret = connect(tcp_sd, &to.in, sizeof(to)); /* IPv4 */
 
674
  }
 
675
  if(ret < 0){
416
676
    perror("connect");
417
 
    return -1;
418
 
  }
419
 
  
420
 
  ret = initgnutls (&es);
421
 
  if (ret != 0){
422
 
    retval = -1;
423
 
    return -1;
424
 
  }
425
 
  
426
 
  gnutls_transport_set_ptr (es.session,
427
 
                            (gnutls_transport_ptr_t) tcp_sd);
 
677
    goto mandos_end;
 
678
  }
 
679
  
 
680
  if(quit_now){
 
681
    goto mandos_end;
 
682
  }
 
683
  
 
684
  const char *out = mandos_protocol_version;
 
685
  written = 0;
 
686
  while(true){
 
687
    size_t out_size = strlen(out);
 
688
    ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
 
689
                                   out_size - written));
 
690
    if(ret == -1){
 
691
      perror("write");
 
692
      goto mandos_end;
 
693
    }
 
694
    written += (size_t)ret;
 
695
    if(written < out_size){
 
696
      continue;
 
697
    } else {
 
698
      if(out == mandos_protocol_version){
 
699
        written = 0;
 
700
        out = "\r\n";
 
701
      } else {
 
702
        break;
 
703
      }
 
704
    }
 
705
  
 
706
    if(quit_now){
 
707
      goto mandos_end;
 
708
    }
 
709
  }
428
710
  
429
711
  if(debug){
430
712
    fprintf(stderr, "Establishing TLS session with %s\n", ip);
431
713
  }
432
714
  
433
 
  ret = gnutls_handshake (es.session);
434
 
  
435
 
  if (ret != GNUTLS_E_SUCCESS){
 
715
  if(quit_now){
 
716
    goto mandos_end;
 
717
  }
 
718
  
 
719
  gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t) tcp_sd);
 
720
  
 
721
  if(quit_now){
 
722
    goto mandos_end;
 
723
  }
 
724
  
 
725
  do {
 
726
    ret = gnutls_handshake(session);
 
727
    if(quit_now){
 
728
      goto mandos_end;
 
729
    }
 
730
  } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
 
731
  
 
732
  if(ret != GNUTLS_E_SUCCESS){
436
733
    if(debug){
437
 
      fprintf(stderr, "\n*** Handshake failed ***\n");
438
 
      gnutls_perror (ret);
 
734
      fprintf(stderr, "*** GnuTLS Handshake failed ***\n");
 
735
      gnutls_perror(ret);
439
736
    }
440
 
    retval = -1;
441
 
    goto exit;
 
737
    goto mandos_end;
442
738
  }
443
739
  
444
 
  //Retrieve OpenPGP packet that contains the wanted password
 
740
  /* Read OpenPGP packet that contains the wanted password */
445
741
  
446
742
  if(debug){
447
 
    fprintf(stderr, "Retrieving pgp encrypted password from %s\n",
 
743
    fprintf(stderr, "Retrieving OpenPGP encrypted password from %s\n",
448
744
            ip);
449
745
  }
450
 
 
 
746
  
451
747
  while(true){
452
 
    if (buffer_length + BUFFER_SIZE > buffer_capacity){
453
 
      buffer = realloc(buffer, buffer_capacity + BUFFER_SIZE);
454
 
      if (buffer == NULL){
455
 
        perror("realloc");
456
 
        goto exit;
457
 
      }
458
 
      buffer_capacity += BUFFER_SIZE;
459
 
    }
460
 
    
461
 
    ret = gnutls_record_recv
462
 
      (es.session, buffer+buffer_length, BUFFER_SIZE);
463
 
    if (ret == 0){
 
748
    
 
749
    if(quit_now){
 
750
      goto mandos_end;
 
751
    }
 
752
    
 
753
    buffer_capacity = incbuffer(&buffer, buffer_length,
 
754
                                   buffer_capacity);
 
755
    if(buffer_capacity == 0){
 
756
      perror("incbuffer");
 
757
      goto mandos_end;
 
758
    }
 
759
    
 
760
    if(quit_now){
 
761
      goto mandos_end;
 
762
    }
 
763
    
 
764
    sret = gnutls_record_recv(session, buffer+buffer_length,
 
765
                              BUFFER_SIZE);
 
766
    if(sret == 0){
464
767
      break;
465
768
    }
466
 
    if (ret < 0){
467
 
      switch(ret){
 
769
    if(sret < 0){
 
770
      switch(sret){
468
771
      case GNUTLS_E_INTERRUPTED:
469
772
      case GNUTLS_E_AGAIN:
470
773
        break;
471
774
      case GNUTLS_E_REHANDSHAKE:
472
 
        ret = gnutls_handshake (es.session);
473
 
        if (ret < 0){
474
 
          fprintf(stderr, "\n*** Handshake failed ***\n");
475
 
          gnutls_perror (ret);
476
 
          retval = -1;
477
 
          goto exit;
 
775
        do {
 
776
          ret = gnutls_handshake(session);
 
777
          
 
778
          if(quit_now){
 
779
            goto mandos_end;
 
780
          }
 
781
        } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
 
782
        if(ret < 0){
 
783
          fprintf(stderr, "*** GnuTLS Re-handshake failed ***\n");
 
784
          gnutls_perror(ret);
 
785
          goto mandos_end;
478
786
        }
479
787
        break;
480
788
      default:
481
789
        fprintf(stderr, "Unknown error while reading data from"
482
 
                " encrypted session with mandos server\n");
483
 
        retval = -1;
484
 
        gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
485
 
        goto exit;
 
790
                " encrypted session with Mandos server\n");
 
791
        gnutls_bye(session, GNUTLS_SHUT_RDWR);
 
792
        goto mandos_end;
486
793
      }
487
794
    } else {
488
 
      buffer_length += (size_t) ret;
489
 
    }
490
 
  }
491
 
  
492
 
  if (buffer_length > 0){
 
795
      buffer_length += (size_t) sret;
 
796
    }
 
797
  }
 
798
  
 
799
  if(debug){
 
800
    fprintf(stderr, "Closing TLS session\n");
 
801
  }
 
802
  
 
803
  if(quit_now){
 
804
    goto mandos_end;
 
805
  }
 
806
  
 
807
  do {
 
808
    ret = gnutls_bye(session, GNUTLS_SHUT_RDWR);
 
809
    if(quit_now){
 
810
      goto mandos_end;
 
811
    }
 
812
  } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
 
813
  
 
814
  if(buffer_length > 0){
 
815
    ssize_t decrypted_buffer_size;
493
816
    decrypted_buffer_size = pgp_packet_decrypt(buffer,
494
817
                                               buffer_length,
495
 
                                               &decrypted_buffer,
496
 
                                               certdir);
497
 
    if (decrypted_buffer_size >= 0){
 
818
                                               &decrypted_buffer);
 
819
    if(decrypted_buffer_size >= 0){
 
820
      
 
821
      written = 0;
498
822
      while(written < (size_t) decrypted_buffer_size){
499
 
        ret = (int)fwrite (decrypted_buffer + written, 1,
500
 
                           (size_t)decrypted_buffer_size - written,
501
 
                           stdout);
 
823
        if(quit_now){
 
824
          goto mandos_end;
 
825
        }
 
826
        
 
827
        ret = (int)fwrite(decrypted_buffer + written, 1,
 
828
                          (size_t)decrypted_buffer_size - written,
 
829
                          stdout);
502
830
        if(ret == 0 and ferror(stdout)){
503
831
          if(debug){
504
832
            fprintf(stderr, "Error writing encrypted data: %s\n",
505
833
                    strerror(errno));
506
834
          }
507
 
          retval = -1;
508
 
          break;
 
835
          goto mandos_end;
509
836
        }
510
837
        written += (size_t)ret;
511
838
      }
512
 
      free(decrypted_buffer);
513
 
    } else {
514
 
      retval = -1;
 
839
      retval = 0;
515
840
    }
516
841
  }
517
 
 
518
 
  //shutdown procedure
519
 
 
520
 
  if(debug){
521
 
    fprintf(stderr, "Closing TLS session\n");
522
 
  }
523
 
 
 
842
  
 
843
  /* Shutdown procedure */
 
844
  
 
845
 mandos_end:
 
846
  free(decrypted_buffer);
524
847
  free(buffer);
525
 
  gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
526
 
 exit:
527
 
  close(tcp_sd);
528
 
  gnutls_deinit (es.session);
529
 
  gnutls_certificate_free_credentials (es.cred);
530
 
  gnutls_global_deinit ();
 
848
  if(tcp_sd >= 0){
 
849
    ret = (int)TEMP_FAILURE_RETRY(close(tcp_sd));
 
850
  }
 
851
  if(ret == -1){
 
852
    perror("close");
 
853
  }
 
854
  gnutls_deinit(session);
 
855
  if(quit_now){
 
856
    retval = -1;
 
857
  }
531
858
  return retval;
532
859
}
533
860
 
534
 
static AvahiSimplePoll *simple_poll = NULL;
535
 
static AvahiServer *server = NULL;
536
 
 
537
 
static void resolve_callback(
538
 
    AvahiSServiceResolver *r,
539
 
    AvahiIfIndex interface,
540
 
    AVAHI_GCC_UNUSED AvahiProtocol protocol,
541
 
    AvahiResolverEvent event,
542
 
    const char *name,
543
 
    const char *type,
544
 
    const char *domain,
545
 
    const char *host_name,
546
 
    const AvahiAddress *address,
547
 
    uint16_t port,
548
 
    AVAHI_GCC_UNUSED AvahiStringList *txt,
549
 
    AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
550
 
    AVAHI_GCC_UNUSED void* userdata) {
551
 
    
552
 
  assert(r);                    /* Spurious warning */
 
861
static void resolve_callback(AvahiSServiceResolver *r,
 
862
                             AvahiIfIndex interface,
 
863
                             AvahiProtocol proto,
 
864
                             AvahiResolverEvent event,
 
865
                             const char *name,
 
866
                             const char *type,
 
867
                             const char *domain,
 
868
                             const char *host_name,
 
869
                             const AvahiAddress *address,
 
870
                             uint16_t port,
 
871
                             AVAHI_GCC_UNUSED AvahiStringList *txt,
 
872
                             AVAHI_GCC_UNUSED AvahiLookupResultFlags
 
873
                             flags,
 
874
                             AVAHI_GCC_UNUSED void* userdata){
 
875
  assert(r);
553
876
  
554
877
  /* Called whenever a service has been resolved successfully or
555
878
     timed out */
556
879
  
557
 
  switch (event) {
 
880
  if(quit_now){
 
881
    return;
 
882
  }
 
883
  
 
884
  switch(event){
558
885
  default:
559
886
  case AVAHI_RESOLVER_FAILURE:
560
 
    fprintf(stderr, "(Resolver) Failed to resolve service '%s' of"
561
 
            " type '%s' in domain '%s': %s\n", name, type, domain,
562
 
            avahi_strerror(avahi_server_errno(server)));
 
887
    fprintf(stderr, "(Avahi Resolver) Failed to resolve service '%s'"
 
888
            " of type '%s' in domain '%s': %s\n", name, type, domain,
 
889
            avahi_strerror(avahi_server_errno(mc.server)));
563
890
    break;
564
891
    
565
892
  case AVAHI_RESOLVER_FOUND:
567
894
      char ip[AVAHI_ADDRESS_STR_MAX];
568
895
      avahi_address_snprint(ip, sizeof(ip), address);
569
896
      if(debug){
570
 
        fprintf(stderr, "Mandos server \"%s\" found on %s (%s) on"
571
 
                " port %d\n", name, host_name, ip, port);
 
897
        fprintf(stderr, "Mandos server \"%s\" found on %s (%s, %"
 
898
                PRIdMAX ") on port %" PRIu16 "\n", name, host_name,
 
899
                ip, (intmax_t)interface, port);
572
900
      }
573
 
      int ret = start_mandos_communication(ip, port, interface);
574
 
      if (ret == 0){
575
 
        exit(EXIT_SUCCESS);
 
901
      int ret = start_mandos_communication(ip, port, interface,
 
902
                                           avahi_proto_to_af(proto));
 
903
      if(ret == 0){
 
904
        avahi_simple_poll_quit(mc.simple_poll);
576
905
      }
577
906
    }
578
907
  }
579
908
  avahi_s_service_resolver_free(r);
580
909
}
581
910
 
582
 
static void browse_callback(
583
 
    AvahiSServiceBrowser *b,
584
 
    AvahiIfIndex interface,
585
 
    AvahiProtocol protocol,
586
 
    AvahiBrowserEvent event,
587
 
    const char *name,
588
 
    const char *type,
589
 
    const char *domain,
590
 
    AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
591
 
    void* userdata) {
592
 
    
593
 
    AvahiServer *s = userdata;
594
 
    assert(b);                  /* Spurious warning */
595
 
    
596
 
    /* Called whenever a new services becomes available on the LAN or
597
 
       is removed from the LAN */
598
 
    
599
 
    switch (event) {
 
911
static void browse_callback(AvahiSServiceBrowser *b,
 
912
                            AvahiIfIndex interface,
 
913
                            AvahiProtocol protocol,
 
914
                            AvahiBrowserEvent event,
 
915
                            const char *name,
 
916
                            const char *type,
 
917
                            const char *domain,
 
918
                            AVAHI_GCC_UNUSED AvahiLookupResultFlags
 
919
                            flags,
 
920
                            AVAHI_GCC_UNUSED void* userdata){
 
921
  assert(b);
 
922
  
 
923
  /* Called whenever a new services becomes available on the LAN or
 
924
     is removed from the LAN */
 
925
  
 
926
  if(quit_now){
 
927
    return;
 
928
  }
 
929
  
 
930
  switch(event){
 
931
  default:
 
932
  case AVAHI_BROWSER_FAILURE:
 
933
    
 
934
    fprintf(stderr, "(Avahi browser) %s\n",
 
935
            avahi_strerror(avahi_server_errno(mc.server)));
 
936
    avahi_simple_poll_quit(mc.simple_poll);
 
937
    return;
 
938
    
 
939
  case AVAHI_BROWSER_NEW:
 
940
    /* We ignore the returned Avahi resolver object. In the callback
 
941
       function we free it. If the Avahi server is terminated before
 
942
       the callback function is called the Avahi server will free the
 
943
       resolver for us. */
 
944
    
 
945
    if(avahi_s_service_resolver_new(mc.server, interface, protocol,
 
946
                                    name, type, domain, protocol, 0,
 
947
                                    resolve_callback, NULL) == NULL)
 
948
      fprintf(stderr, "Avahi: Failed to resolve service '%s': %s\n",
 
949
              name, avahi_strerror(avahi_server_errno(mc.server)));
 
950
    break;
 
951
    
 
952
  case AVAHI_BROWSER_REMOVE:
 
953
    break;
 
954
    
 
955
  case AVAHI_BROWSER_ALL_FOR_NOW:
 
956
  case AVAHI_BROWSER_CACHE_EXHAUSTED:
 
957
    if(debug){
 
958
      fprintf(stderr, "No Mandos server found, still searching...\n");
 
959
    }
 
960
    break;
 
961
  }
 
962
}
 
963
 
 
964
/* stop main loop after sigterm has been called */
 
965
static void handle_sigterm(int sig){
 
966
  if(quit_now){
 
967
    return;
 
968
  }
 
969
  quit_now = 1;
 
970
  signal_received = sig;
 
971
  int old_errno = errno;
 
972
  if(mc.simple_poll != NULL){
 
973
    avahi_simple_poll_quit(mc.simple_poll);
 
974
  }
 
975
  errno = old_errno;
 
976
}
 
977
 
 
978
int main(int argc, char *argv[]){
 
979
  AvahiSServiceBrowser *sb = NULL;
 
980
  int error;
 
981
  int ret;
 
982
  intmax_t tmpmax;
 
983
  char *tmp;
 
984
  int exitcode = EXIT_SUCCESS;
 
985
  const char *interface = "eth0";
 
986
  struct ifreq network;
 
987
  int sd = -1;
 
988
  bool take_down_interface = false;
 
989
  uid_t uid;
 
990
  gid_t gid;
 
991
  char *connect_to = NULL;
 
992
  char tempdir[] = "/tmp/mandosXXXXXX";
 
993
  bool tempdir_created = false;
 
994
  AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
 
995
  const char *seckey = PATHDIR "/" SECKEY;
 
996
  const char *pubkey = PATHDIR "/" PUBKEY;
 
997
  
 
998
  bool gnutls_initialized = false;
 
999
  bool gpgme_initialized = false;
 
1000
  float delay = 2.5f;
 
1001
  
 
1002
  struct sigaction old_sigterm_action = { .sa_handler = SIG_DFL };
 
1003
  struct sigaction sigterm_action = { .sa_handler = handle_sigterm };
 
1004
  
 
1005
  uid = getuid();
 
1006
  gid = getgid();
 
1007
  
 
1008
  /* Lower any group privileges we might have, just to be safe */
 
1009
  errno = 0;
 
1010
  ret = setgid(gid);
 
1011
  if(ret == -1){
 
1012
    perror("setgid");
 
1013
  }
 
1014
  
 
1015
  /* Lower user privileges (temporarily) */
 
1016
  errno = 0;
 
1017
  ret = seteuid(uid);
 
1018
  if(ret == -1){
 
1019
    perror("seteuid");
 
1020
  }
 
1021
  
 
1022
  if(quit_now){
 
1023
    goto end;
 
1024
  }
 
1025
  
 
1026
  {
 
1027
    struct argp_option options[] = {
 
1028
      { .name = "debug", .key = 128,
 
1029
        .doc = "Debug mode", .group = 3 },
 
1030
      { .name = "connect", .key = 'c',
 
1031
        .arg = "ADDRESS:PORT",
 
1032
        .doc = "Connect directly to a specific Mandos server",
 
1033
        .group = 1 },
 
1034
      { .name = "interface", .key = 'i',
 
1035
        .arg = "NAME",
 
1036
        .doc = "Network interface that will be used to search for"
 
1037
        " Mandos servers",
 
1038
        .group = 1 },
 
1039
      { .name = "seckey", .key = 's',
 
1040
        .arg = "FILE",
 
1041
        .doc = "OpenPGP secret key file base name",
 
1042
        .group = 1 },
 
1043
      { .name = "pubkey", .key = 'p',
 
1044
        .arg = "FILE",
 
1045
        .doc = "OpenPGP public key file base name",
 
1046
        .group = 2 },
 
1047
      { .name = "dh-bits", .key = 129,
 
1048
        .arg = "BITS",
 
1049
        .doc = "Bit length of the prime number used in the"
 
1050
        " Diffie-Hellman key exchange",
 
1051
        .group = 2 },
 
1052
      { .name = "priority", .key = 130,
 
1053
        .arg = "STRING",
 
1054
        .doc = "GnuTLS priority string for the TLS handshake",
 
1055
        .group = 1 },
 
1056
      { .name = "delay", .key = 131,
 
1057
        .arg = "SECONDS",
 
1058
        .doc = "Maximum delay to wait for interface startup",
 
1059
        .group = 2 },
 
1060
      /*
 
1061
       * These reproduce what we would get without ARGP_NO_HELP
 
1062
       */
 
1063
      { .name = "help", .key = '?',
 
1064
        .doc = "Give this help list", .group = -1 },
 
1065
      { .name = "usage", .key = -3,
 
1066
        .doc = "Give a short usage message", .group = -1 },
 
1067
      { .name = "version", .key = 'V',
 
1068
        .doc = "Print program version", .group = -1 },
 
1069
      { .name = NULL }
 
1070
    };
 
1071
    
 
1072
    error_t parse_opt(int key, char *arg,
 
1073
                      struct argp_state *state){
 
1074
      errno = 0;
 
1075
      switch(key){
 
1076
      case 128:                 /* --debug */
 
1077
        debug = true;
 
1078
        break;
 
1079
      case 'c':                 /* --connect */
 
1080
        connect_to = arg;
 
1081
        break;
 
1082
      case 'i':                 /* --interface */
 
1083
        interface = arg;
 
1084
        break;
 
1085
      case 's':                 /* --seckey */
 
1086
        seckey = arg;
 
1087
        break;
 
1088
      case 'p':                 /* --pubkey */
 
1089
        pubkey = arg;
 
1090
        break;
 
1091
      case 129:                 /* --dh-bits */
 
1092
        errno = 0;
 
1093
        tmpmax = strtoimax(arg, &tmp, 10);
 
1094
        if(errno != 0 or tmp == arg or *tmp != '\0'
 
1095
           or tmpmax != (typeof(mc.dh_bits))tmpmax){
 
1096
          argp_error(state, "Bad number of DH bits");
 
1097
        }
 
1098
        mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
 
1099
        break;
 
1100
      case 130:                 /* --priority */
 
1101
        mc.priority = arg;
 
1102
        break;
 
1103
      case 131:                 /* --delay */
 
1104
        errno = 0;
 
1105
        delay = strtof(arg, &tmp);
 
1106
        if(errno != 0 or tmp == arg or *tmp != '\0'){
 
1107
          argp_error(state, "Bad delay");
 
1108
        }
 
1109
        break;
 
1110
        /*
 
1111
         * These reproduce what we would get without ARGP_NO_HELP
 
1112
         */
 
1113
      case '?':                 /* --help */
 
1114
        argp_state_help(state, state->out_stream,
 
1115
                        (ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR)
 
1116
                        & ~(unsigned int)ARGP_HELP_EXIT_OK);
 
1117
      case -3:                  /* --usage */
 
1118
        argp_state_help(state, state->out_stream,
 
1119
                        ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
 
1120
      case 'V':                 /* --version */
 
1121
        fprintf(state->out_stream, "%s\n", argp_program_version);
 
1122
        exit(argp_err_exit_status);
 
1123
        break;
 
1124
      default:
 
1125
        return ARGP_ERR_UNKNOWN;
 
1126
      }
 
1127
      return errno;
 
1128
    }
 
1129
    
 
1130
    struct argp argp = { .options = options, .parser = parse_opt,
 
1131
                         .args_doc = "",
 
1132
                         .doc = "Mandos client -- Get and decrypt"
 
1133
                         " passwords from a Mandos server" };
 
1134
    ret = argp_parse(&argp, argc, argv,
 
1135
                     ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
 
1136
    switch(ret){
 
1137
    case 0:
 
1138
      break;
 
1139
    case ENOMEM:
600
1140
    default:
601
 
    case AVAHI_BROWSER_FAILURE:
602
 
      
603
 
      fprintf(stderr, "(Browser) %s\n",
604
 
              avahi_strerror(avahi_server_errno(server)));
605
 
      avahi_simple_poll_quit(simple_poll);
606
 
      return;
607
 
      
608
 
    case AVAHI_BROWSER_NEW:
609
 
      /* We ignore the returned resolver object. In the callback
610
 
         function we free it. If the server is terminated before
611
 
         the callback function is called the server will free
612
 
         the resolver for us. */
613
 
      
614
 
      if (!(avahi_s_service_resolver_new(s, interface, protocol, name,
615
 
                                         type, domain,
616
 
                                         AVAHI_PROTO_INET6, 0,
617
 
                                         resolve_callback, s)))
618
 
        fprintf(stderr, "Failed to resolve service '%s': %s\n", name,
619
 
                avahi_strerror(avahi_server_errno(s)));
620
 
      break;
621
 
      
622
 
    case AVAHI_BROWSER_REMOVE:
623
 
      break;
624
 
      
625
 
    case AVAHI_BROWSER_ALL_FOR_NOW:
626
 
    case AVAHI_BROWSER_CACHE_EXHAUSTED:
627
 
      break;
628
 
    }
629
 
}
630
 
 
631
 
/* combinds file name and path and returns the malloced new string. som sane checks could/should be added */
632
 
const char *combinepath(const char *first, const char *second){
633
 
  char *tmp;
634
 
  tmp = malloc(strlen(first) + strlen(second) + 2);
635
 
  if (tmp == NULL){
636
 
    perror("malloc");
637
 
    return NULL;
638
 
  }
639
 
  strcpy(tmp, first);
640
 
  if (first[0] != '\0' and first[strlen(first) - 1] != '/'){
641
 
    strcat(tmp, "/");
642
 
  }
643
 
  strcat(tmp, second);
644
 
  return tmp;
645
 
}
646
 
 
647
 
 
648
 
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
 
1141
      errno = ret;
 
1142
      perror("argp_parse");
 
1143
      exitcode = EX_OSERR;
 
1144
      goto end;
 
1145
    case EINVAL:
 
1146
      exitcode = EX_USAGE;
 
1147
      goto end;
 
1148
    }
 
1149
  }
 
1150
  
 
1151
  if(not debug){
 
1152
    avahi_set_log_function(empty_log);
 
1153
  }
 
1154
  
 
1155
  /* Initialize Avahi early so avahi_simple_poll_quit() can be called
 
1156
     from the signal handler */
 
1157
  /* Initialize the pseudo-RNG for Avahi */
 
1158
  srand((unsigned int) time(NULL));
 
1159
  mc.simple_poll = avahi_simple_poll_new();
 
1160
  if(mc.simple_poll == NULL){
 
1161
    fprintf(stderr, "Avahi: Failed to create simple poll object.\n");
 
1162
    exitcode = EXIT_FAILURE;
 
1163
    goto end;
 
1164
  }
 
1165
  
 
1166
  sigemptyset(&sigterm_action.sa_mask);
 
1167
  ret = sigaddset(&sigterm_action.sa_mask, SIGINT);
 
1168
  if(ret == -1){
 
1169
    perror("sigaddset");
 
1170
    exitcode = EXIT_FAILURE;
 
1171
    goto end;
 
1172
  }
 
1173
  ret = sigaddset(&sigterm_action.sa_mask, SIGHUP);
 
1174
  if(ret == -1){
 
1175
    perror("sigaddset");
 
1176
    exitcode = EXIT_FAILURE;
 
1177
    goto end;
 
1178
  }
 
1179
  ret = sigaddset(&sigterm_action.sa_mask, SIGTERM);
 
1180
  if(ret == -1){
 
1181
    perror("sigaddset");
 
1182
    exitcode = EXIT_FAILURE;
 
1183
    goto end;
 
1184
  }
 
1185
  /* Need to check if the handler is SIG_IGN before handling:
 
1186
     | [[info:libc:Initial Signal Actions]] |
 
1187
     | [[info:libc:Basic Signal Handling]]  |
 
1188
  */
 
1189
  ret = sigaction(SIGINT, NULL, &old_sigterm_action);
 
1190
  if(ret == -1){
 
1191
    perror("sigaction");
 
1192
    return EXIT_FAILURE;
 
1193
  }
 
1194
  if(old_sigterm_action.sa_handler != SIG_IGN){
 
1195
    ret = sigaction(SIGINT, &sigterm_action, NULL);
 
1196
    if(ret == -1){
 
1197
      perror("sigaction");
 
1198
      exitcode = EXIT_FAILURE;
 
1199
      goto end;
 
1200
    }
 
1201
  }
 
1202
  ret = sigaction(SIGHUP, NULL, &old_sigterm_action);
 
1203
  if(ret == -1){
 
1204
    perror("sigaction");
 
1205
    return EXIT_FAILURE;
 
1206
  }
 
1207
  if(old_sigterm_action.sa_handler != SIG_IGN){
 
1208
    ret = sigaction(SIGHUP, &sigterm_action, NULL);
 
1209
    if(ret == -1){
 
1210
      perror("sigaction");
 
1211
      exitcode = EXIT_FAILURE;
 
1212
      goto end;
 
1213
    }
 
1214
  }
 
1215
  ret = sigaction(SIGTERM, NULL, &old_sigterm_action);
 
1216
  if(ret == -1){
 
1217
    perror("sigaction");
 
1218
    return EXIT_FAILURE;
 
1219
  }
 
1220
  if(old_sigterm_action.sa_handler != SIG_IGN){
 
1221
    ret = sigaction(SIGTERM, &sigterm_action, NULL);
 
1222
    if(ret == -1){
 
1223
      perror("sigaction");
 
1224
      exitcode = EXIT_FAILURE;
 
1225
      goto end;
 
1226
    }
 
1227
  }
 
1228
  
 
1229
  /* If the interface is down, bring it up */
 
1230
  if(interface[0] != '\0'){
 
1231
    if_index = (AvahiIfIndex) if_nametoindex(interface);
 
1232
    if(if_index == 0){
 
1233
      fprintf(stderr, "No such interface: \"%s\"\n", interface);
 
1234
      exitcode = EXIT_FAILURE;
 
1235
      goto end;
 
1236
    }
 
1237
    
 
1238
    if(quit_now){
 
1239
      goto end;
 
1240
    }
 
1241
    
 
1242
    /* Re-raise priviliges */
 
1243
    errno = 0;
 
1244
    ret = seteuid(0);
 
1245
    if(ret == -1){
 
1246
      perror("seteuid");
 
1247
    }
 
1248
    
 
1249
#ifdef __linux__
 
1250
    /* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
 
1251
       messages about the network interface to mess up the prompt */
 
1252
    ret = klogctl(8, NULL, 5);
 
1253
    bool restore_loglevel = true;
 
1254
    if(ret == -1){
 
1255
      restore_loglevel = false;
 
1256
      perror("klogctl");
 
1257
    }
 
1258
#endif  /* __linux__ */
 
1259
    
 
1260
    sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
 
1261
    if(sd < 0){
 
1262
      perror("socket");
 
1263
      exitcode = EXIT_FAILURE;
 
1264
#ifdef __linux__
 
1265
      if(restore_loglevel){
 
1266
        ret = klogctl(7, NULL, 0);
 
1267
        if(ret == -1){
 
1268
          perror("klogctl");
 
1269
        }
 
1270
      }
 
1271
#endif  /* __linux__ */
 
1272
      /* Lower privileges */
 
1273
      errno = 0;
 
1274
      ret = seteuid(uid);
 
1275
      if(ret == -1){
 
1276
        perror("seteuid");
 
1277
      }
 
1278
      goto end;
 
1279
    }
 
1280
    strcpy(network.ifr_name, interface);
 
1281
    ret = ioctl(sd, SIOCGIFFLAGS, &network);
 
1282
    if(ret == -1){
 
1283
      perror("ioctl SIOCGIFFLAGS");
 
1284
#ifdef __linux__
 
1285
      if(restore_loglevel){
 
1286
        ret = klogctl(7, NULL, 0);
 
1287
        if(ret == -1){
 
1288
          perror("klogctl");
 
1289
        }
 
1290
      }
 
1291
#endif  /* __linux__ */
 
1292
      exitcode = EXIT_FAILURE;
 
1293
      /* Lower privileges */
 
1294
      errno = 0;
 
1295
      ret = seteuid(uid);
 
1296
      if(ret == -1){
 
1297
        perror("seteuid");
 
1298
      }
 
1299
      goto end;
 
1300
    }
 
1301
    if((network.ifr_flags & IFF_UP) == 0){
 
1302
      network.ifr_flags |= IFF_UP;
 
1303
      take_down_interface = true;
 
1304
      ret = ioctl(sd, SIOCSIFFLAGS, &network);
 
1305
      if(ret == -1){
 
1306
        take_down_interface = false;
 
1307
        perror("ioctl SIOCSIFFLAGS");
 
1308
        exitcode = EXIT_FAILURE;
 
1309
#ifdef __linux__
 
1310
        if(restore_loglevel){
 
1311
          ret = klogctl(7, NULL, 0);
 
1312
          if(ret == -1){
 
1313
            perror("klogctl");
 
1314
          }
 
1315
        }
 
1316
#endif  /* __linux__ */
 
1317
        /* Lower privileges */
 
1318
        errno = 0;
 
1319
        ret = seteuid(uid);
 
1320
        if(ret == -1){
 
1321
          perror("seteuid");
 
1322
        }
 
1323
        goto end;
 
1324
      }
 
1325
    }
 
1326
    /* sleep checking until interface is running */
 
1327
    for(int i=0; i < delay * 4; i++){
 
1328
      ret = ioctl(sd, SIOCGIFFLAGS, &network);
 
1329
      if(ret == -1){
 
1330
        perror("ioctl SIOCGIFFLAGS");
 
1331
      } else if(network.ifr_flags & IFF_RUNNING){
 
1332
        break;
 
1333
      }
 
1334
      struct timespec sleeptime = { .tv_nsec = 250000000 };
 
1335
      ret = nanosleep(&sleeptime, NULL);
 
1336
      if(ret == -1 and errno != EINTR){
 
1337
        perror("nanosleep");
 
1338
      }
 
1339
    }
 
1340
    if(not take_down_interface){
 
1341
      /* We won't need the socket anymore */
 
1342
      ret = (int)TEMP_FAILURE_RETRY(close(sd));
 
1343
      if(ret == -1){
 
1344
        perror("close");
 
1345
      }
 
1346
    }
 
1347
#ifdef __linux__
 
1348
    if(restore_loglevel){
 
1349
      /* Restores kernel loglevel to default */
 
1350
      ret = klogctl(7, NULL, 0);
 
1351
      if(ret == -1){
 
1352
        perror("klogctl");
 
1353
      }
 
1354
    }
 
1355
#endif  /* __linux__ */
 
1356
    /* Lower privileges */
 
1357
    errno = 0;
 
1358
    if(take_down_interface){
 
1359
      /* Lower privileges */
 
1360
      ret = seteuid(uid);
 
1361
      if(ret == -1){
 
1362
        perror("seteuid");
 
1363
      }
 
1364
    } else {
 
1365
      /* Lower privileges permanently */
 
1366
      ret = setuid(uid);
 
1367
      if(ret == -1){
 
1368
        perror("setuid");
 
1369
      }
 
1370
    }
 
1371
  }
 
1372
  
 
1373
  if(quit_now){
 
1374
    goto end;
 
1375
  }
 
1376
  
 
1377
  ret = init_gnutls_global(pubkey, seckey);
 
1378
  if(ret == -1){
 
1379
    fprintf(stderr, "init_gnutls_global failed\n");
 
1380
    exitcode = EXIT_FAILURE;
 
1381
    goto end;
 
1382
  } else {
 
1383
    gnutls_initialized = true;
 
1384
  }
 
1385
  
 
1386
  if(quit_now){
 
1387
    goto end;
 
1388
  }
 
1389
  
 
1390
  tempdir_created = true;
 
1391
  if(mkdtemp(tempdir) == NULL){
 
1392
    tempdir_created = false;
 
1393
    perror("mkdtemp");
 
1394
    goto end;
 
1395
  }
 
1396
  
 
1397
  if(quit_now){
 
1398
    goto end;
 
1399
  }
 
1400
  
 
1401
  if(not init_gpgme(pubkey, seckey, tempdir)){
 
1402
    fprintf(stderr, "init_gpgme failed\n");
 
1403
    exitcode = EXIT_FAILURE;
 
1404
    goto end;
 
1405
  } else {
 
1406
    gpgme_initialized = true;
 
1407
  }
 
1408
  
 
1409
  if(quit_now){
 
1410
    goto end;
 
1411
  }
 
1412
  
 
1413
  if(connect_to != NULL){
 
1414
    /* Connect directly, do not use Zeroconf */
 
1415
    /* (Mainly meant for debugging) */
 
1416
    char *address = strrchr(connect_to, ':');
 
1417
    if(address == NULL){
 
1418
      fprintf(stderr, "No colon in address\n");
 
1419
      exitcode = EXIT_FAILURE;
 
1420
      goto end;
 
1421
    }
 
1422
    
 
1423
    if(quit_now){
 
1424
      goto end;
 
1425
    }
 
1426
    
 
1427
    uint16_t port;
 
1428
    errno = 0;
 
1429
    tmpmax = strtoimax(address+1, &tmp, 10);
 
1430
    if(errno != 0 or tmp == address+1 or *tmp != '\0'
 
1431
       or tmpmax != (uint16_t)tmpmax){
 
1432
      fprintf(stderr, "Bad port number\n");
 
1433
      exitcode = EXIT_FAILURE;
 
1434
      goto end;
 
1435
    }
 
1436
  
 
1437
    if(quit_now){
 
1438
      goto end;
 
1439
    }
 
1440
    
 
1441
    port = (uint16_t)tmpmax;
 
1442
    *address = '\0';
 
1443
    address = connect_to;
 
1444
    /* Colon in address indicates IPv6 */
 
1445
    int af;
 
1446
    if(strchr(address, ':') != NULL){
 
1447
      af = AF_INET6;
 
1448
    } else {
 
1449
      af = AF_INET;
 
1450
    }
 
1451
    
 
1452
    if(quit_now){
 
1453
      goto end;
 
1454
    }
 
1455
    
 
1456
    ret = start_mandos_communication(address, port, if_index, af);
 
1457
    if(ret < 0){
 
1458
      exitcode = EXIT_FAILURE;
 
1459
    } else {
 
1460
      exitcode = EXIT_SUCCESS;
 
1461
    }
 
1462
    goto end;
 
1463
  }
 
1464
  
 
1465
  if(quit_now){
 
1466
    goto end;
 
1467
  }
 
1468
  
 
1469
  {
649
1470
    AvahiServerConfig config;
650
 
    AvahiSServiceBrowser *sb = NULL;
651
 
    int error;
652
 
    int ret;
653
 
    int returncode = EXIT_SUCCESS;
654
 
    const char *interface = NULL;
655
 
    AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
656
 
    char *connect_to = NULL;
657
 
    
658
 
    while (true){
659
 
      static struct option long_options[] = {
660
 
        {"debug", no_argument, (int *)&debug, 1},
661
 
        {"connect", required_argument, 0, 'C'},
662
 
        {"interface", required_argument, 0, 'i'},
663
 
        {"certdir", required_argument, 0, 'd'},
664
 
        {"certkey", required_argument, 0, 'c'},
665
 
        {"certfile", required_argument, 0, 'k'},
666
 
        {0, 0, 0, 0} };
667
 
      
668
 
      int option_index = 0;
669
 
      ret = getopt_long (argc, argv, "i:", long_options,
670
 
                         &option_index);
671
 
      
672
 
      if (ret == -1){
673
 
        break;
674
 
      }
675
 
      
676
 
      switch(ret){
677
 
      case 0:
678
 
        break;
679
 
      case 'i':
680
 
        interface = optarg;
681
 
        break;
682
 
      case 'C':
683
 
        connect_to = optarg;
684
 
        break;
685
 
      case 'd':
686
 
        certdir = optarg;
687
 
        break;
688
 
      case 'c':
689
 
        certfile = optarg;
690
 
        break;
691
 
      case 'k':
692
 
        certkey = optarg;
693
 
        break;
694
 
      default:
695
 
        exit(EXIT_FAILURE);
696
 
      }
697
 
    }
698
 
 
699
 
    certfile = combinepath(certdir, certfile);
700
 
    if (certfile == NULL){
701
 
      goto exit;
702
 
    }
703
 
    
704
 
    if(interface != NULL){
705
 
      if_index = (AvahiIfIndex) if_nametoindex(interface);
706
 
      if(if_index == 0){
707
 
        fprintf(stderr, "No such interface: \"%s\"\n", interface);
708
 
        exit(EXIT_FAILURE);
709
 
      }
710
 
    }
711
 
    
712
 
    if(connect_to != NULL){
713
 
      /* Connect directly, do not use Zeroconf */
714
 
      /* (Mainly meant for debugging) */
715
 
      char *address = strrchr(connect_to, ':');
716
 
      if(address == NULL){
717
 
        fprintf(stderr, "No colon in address\n");
718
 
        exit(EXIT_FAILURE);
719
 
      }
720
 
      errno = 0;
721
 
      uint16_t port = (uint16_t) strtol(address+1, NULL, 10);
722
 
      if(errno){
723
 
        perror("Bad port number");
724
 
        exit(EXIT_FAILURE);
725
 
      }
726
 
      *address = '\0';
727
 
      address = connect_to;
728
 
      ret = start_mandos_communication(address, port, if_index);
729
 
      if(ret < 0){
730
 
        exit(EXIT_FAILURE);
731
 
      } else {
732
 
        exit(EXIT_SUCCESS);
733
 
      }
734
 
    }
735
 
    
736
 
    certkey = combinepath(certdir, certkey);
737
 
    if (certkey == NULL){
738
 
      goto exit;
739
 
    }
740
 
    
741
 
    if (not debug){
742
 
      avahi_set_log_function(empty_log);
743
 
    }
744
 
    
745
 
    /* Initialize the psuedo-RNG */
746
 
    srand((unsigned int) time(NULL));
747
 
 
748
 
    /* Allocate main loop object */
749
 
    if (!(simple_poll = avahi_simple_poll_new())) {
750
 
        fprintf(stderr, "Failed to create simple poll object.\n");
751
 
        
752
 
        goto exit;
753
 
    }
754
 
 
755
 
    /* Do not publish any local records */
 
1471
    /* Do not publish any local Zeroconf records */
756
1472
    avahi_server_config_init(&config);
757
1473
    config.publish_hinfo = 0;
758
1474
    config.publish_addresses = 0;
759
1475
    config.publish_workstation = 0;
760
1476
    config.publish_domain = 0;
761
 
 
 
1477
    
762
1478
    /* Allocate a new server */
763
 
    server = avahi_server_new(avahi_simple_poll_get(simple_poll),
764
 
                              &config, NULL, NULL, &error);
765
 
 
766
 
    /* Free the configuration data */
 
1479
    mc.server = avahi_server_new(avahi_simple_poll_get
 
1480
                                 (mc.simple_poll), &config, NULL,
 
1481
                                 NULL, &error);
 
1482
    
 
1483
    /* Free the Avahi configuration data */
767
1484
    avahi_server_config_free(&config);
768
 
 
769
 
    /* Check if creating the server object succeeded */
770
 
    if (!server) {
771
 
        fprintf(stderr, "Failed to create server: %s\n",
772
 
                avahi_strerror(error));
773
 
        returncode = EXIT_FAILURE;
774
 
        goto exit;
775
 
    }
776
 
    
777
 
    /* Create the service browser */
778
 
    sb = avahi_s_service_browser_new(server, if_index,
779
 
                                     AVAHI_PROTO_INET6,
780
 
                                     "_mandos._tcp", NULL, 0,
781
 
                                     browse_callback, server);
782
 
    if (!sb) {
783
 
        fprintf(stderr, "Failed to create service browser: %s\n",
784
 
                avahi_strerror(avahi_server_errno(server)));
785
 
        returncode = EXIT_FAILURE;
786
 
        goto exit;
787
 
    }
788
 
    
789
 
    /* Run the main loop */
790
 
 
791
 
    if (debug){
792
 
      fprintf(stderr, "Starting avahi loop search\n");
793
 
    }
794
 
    
795
 
    avahi_simple_poll_loop(simple_poll);
796
 
    
797
 
 exit:
798
 
 
799
 
    if (debug){
800
 
      fprintf(stderr, "%s exiting\n", argv[0]);
801
 
    }
802
 
    
803
 
    /* Cleanup things */
804
 
    if (sb)
805
 
        avahi_s_service_browser_free(sb);
806
 
    
807
 
    if (server)
808
 
        avahi_server_free(server);
809
 
 
810
 
    if (simple_poll)
811
 
        avahi_simple_poll_free(simple_poll);
812
 
    free(certfile);
813
 
    free(certkey);
814
 
    
815
 
    return returncode;
 
1485
  }
 
1486
  
 
1487
  /* Check if creating the Avahi server object succeeded */
 
1488
  if(mc.server == NULL){
 
1489
    fprintf(stderr, "Failed to create Avahi server: %s\n",
 
1490
            avahi_strerror(error));
 
1491
    exitcode = EXIT_FAILURE;
 
1492
    goto end;
 
1493
  }
 
1494
  
 
1495
  if(quit_now){
 
1496
    goto end;
 
1497
  }
 
1498
  
 
1499
  /* Create the Avahi service browser */
 
1500
  sb = avahi_s_service_browser_new(mc.server, if_index,
 
1501
                                   AVAHI_PROTO_UNSPEC, "_mandos._tcp",
 
1502
                                   NULL, 0, browse_callback, NULL);
 
1503
  if(sb == NULL){
 
1504
    fprintf(stderr, "Failed to create service browser: %s\n",
 
1505
            avahi_strerror(avahi_server_errno(mc.server)));
 
1506
    exitcode = EXIT_FAILURE;
 
1507
    goto end;
 
1508
  }
 
1509
  
 
1510
  if(quit_now){
 
1511
    goto end;
 
1512
  }
 
1513
  
 
1514
  /* Run the main loop */
 
1515
  
 
1516
  if(debug){
 
1517
    fprintf(stderr, "Starting Avahi loop search\n");
 
1518
  }
 
1519
  
 
1520
  avahi_simple_poll_loop(mc.simple_poll);
 
1521
  
 
1522
 end:
 
1523
  
 
1524
  if(debug){
 
1525
    fprintf(stderr, "%s exiting\n", argv[0]);
 
1526
  }
 
1527
  
 
1528
  /* Cleanup things */
 
1529
  if(sb != NULL)
 
1530
    avahi_s_service_browser_free(sb);
 
1531
  
 
1532
  if(mc.server != NULL)
 
1533
    avahi_server_free(mc.server);
 
1534
  
 
1535
  if(mc.simple_poll != NULL)
 
1536
    avahi_simple_poll_free(mc.simple_poll);
 
1537
  
 
1538
  if(gnutls_initialized){
 
1539
    gnutls_certificate_free_credentials(mc.cred);
 
1540
    gnutls_global_deinit();
 
1541
    gnutls_dh_params_deinit(mc.dh_params);
 
1542
  }
 
1543
  
 
1544
  if(gpgme_initialized){
 
1545
    gpgme_release(mc.ctx);
 
1546
  }
 
1547
  
 
1548
  /* Take down the network interface */
 
1549
  if(take_down_interface){
 
1550
    /* Re-raise priviliges */
 
1551
    errno = 0;
 
1552
    ret = seteuid(0);
 
1553
    if(ret == -1){
 
1554
      perror("seteuid");
 
1555
    }
 
1556
    if(geteuid() == 0){
 
1557
      ret = ioctl(sd, SIOCGIFFLAGS, &network);
 
1558
      if(ret == -1){
 
1559
        perror("ioctl SIOCGIFFLAGS");
 
1560
      } else if(network.ifr_flags & IFF_UP) {
 
1561
        network.ifr_flags &= ~(short)IFF_UP; /* clear flag */
 
1562
        ret = ioctl(sd, SIOCSIFFLAGS, &network);
 
1563
        if(ret == -1){
 
1564
          perror("ioctl SIOCSIFFLAGS");
 
1565
        }
 
1566
      }
 
1567
      ret = (int)TEMP_FAILURE_RETRY(close(sd));
 
1568
      if(ret == -1){
 
1569
        perror("close");
 
1570
      }
 
1571
      /* Lower privileges permanently */
 
1572
      errno = 0;
 
1573
      ret = setuid(uid);
 
1574
      if(ret == -1){
 
1575
        perror("setuid");
 
1576
      }
 
1577
    }
 
1578
  }
 
1579
  
 
1580
  /* Removes the temp directory used by GPGME */
 
1581
  if(tempdir_created){
 
1582
    DIR *d;
 
1583
    struct dirent *direntry;
 
1584
    d = opendir(tempdir);
 
1585
    if(d == NULL){
 
1586
      if(errno != ENOENT){
 
1587
        perror("opendir");
 
1588
      }
 
1589
    } else {
 
1590
      while(true){
 
1591
        direntry = readdir(d);
 
1592
        if(direntry == NULL){
 
1593
          break;
 
1594
        }
 
1595
        /* Skip "." and ".." */
 
1596
        if(direntry->d_name[0] == '.'
 
1597
           and (direntry->d_name[1] == '\0'
 
1598
                or (direntry->d_name[1] == '.'
 
1599
                    and direntry->d_name[2] == '\0'))){
 
1600
          continue;
 
1601
        }
 
1602
        char *fullname = NULL;
 
1603
        ret = asprintf(&fullname, "%s/%s", tempdir,
 
1604
                       direntry->d_name);
 
1605
        if(ret < 0){
 
1606
          perror("asprintf");
 
1607
          continue;
 
1608
        }
 
1609
        ret = remove(fullname);
 
1610
        if(ret == -1){
 
1611
          fprintf(stderr, "remove(\"%s\"): %s\n", fullname,
 
1612
                  strerror(errno));
 
1613
        }
 
1614
        free(fullname);
 
1615
      }
 
1616
      closedir(d);
 
1617
    }
 
1618
    ret = rmdir(tempdir);
 
1619
    if(ret == -1 and errno != ENOENT){
 
1620
      perror("rmdir");
 
1621
    }
 
1622
  }
 
1623
  
 
1624
  if(quit_now){
 
1625
    sigemptyset(&old_sigterm_action.sa_mask);
 
1626
    old_sigterm_action.sa_handler = SIG_DFL;
 
1627
    ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received,
 
1628
                                            &old_sigterm_action,
 
1629
                                            NULL));
 
1630
    if(ret == -1){
 
1631
      perror("sigaction");
 
1632
    }
 
1633
    do {
 
1634
      ret = raise(signal_received);
 
1635
    } while(ret != 0 and errno == EINTR);
 
1636
    if(ret != 0){
 
1637
      perror("raise");
 
1638
      abort();
 
1639
    }
 
1640
    TEMP_FAILURE_RETRY(pause());
 
1641
  }
 
1642
  
 
1643
  return exitcode;
816
1644
}