/mandos/trunk

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

« back to all changes in this revision

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

merge

Show diffs side-by-side

added added

removed removed

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