/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

  • Committer: teddy at bsnet
  • Date: 2010-08-11 20:06:18 UTC
  • mto: (237.2.182 mandos-local)
  • mto: This revision was merged to the branch mainline in revision 270.
  • Revision ID: teddy@fukt.bsnet.se-20100811200618-1uuv7p6cmrb2cmxu
* plugins.d/mandos-client.c: Added debug output.

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