/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 Hogeborn
  • Date: 2015-05-23 20:18:34 UTC
  • mto: (237.7.304 trunk)
  • mto: This revision was merged to the branch mainline in revision 325.
  • Revision ID: teddy@recompile.se-20150523201834-e89ex4ito93yni8x
mandos: Use multiprocessing module to run checkers.

For a long time, the Mandos server has occasionally logged the message
"ERROR: Child process vanished".  This was never a fatal error, but it
has been annoying and slightly worrying, since a definite cause was
not found.  One potential cause could be the "multiprocessing" and
"subprocess" modules conflicting w.r.t. SIGCHLD.  To avoid this,
change the running of checkers from using subprocess.Popen
asynchronously to instead first create a multiprocessing.Process()
(which is asynchronous) calling a function, and have that function
then call subprocess.call() (which is synchronous).  In this way, the
only thing using any asynchronous subprocesses is the multiprocessing
module.

This makes it necessary to change one small thing in the D-Bus API,
since the subprocesses.call() function does not expose the raw wait(2)
status value.

DBUS-API (CheckerCompleted): Change the second value provided by this
                             D-Bus signal from the raw wait(2) status
                             to the actual terminating signal number.
mandos (subprocess_call_pipe): New function to be called by
                               multiprocessing.Process (starting a
                               separate process).
(Client.last_checker signal): New attribute for signal which
                              terminated last checker.  Like
                              last_checker_status, only not accessible
                              via D-Bus.
(Client.checker_callback): Take new "connection" argument and use it
                           to get returncode; set last_checker_signal.
                           Return False so gobject does not call this
                           callback again.
(Client.start_checker): Start checker using a multiprocessing.Process
                        instead of a subprocess.Popen.
(ClientDBus.checker_callback): Take new "connection" argument.        Call
                               Client.checker_callback early to have
                               it set last_checker_status and
                               last_checker_signal; use those.  Change
                               second value provided to D-Bus signal
                               CheckerCompleted to use
                               last_checker_signal if checker was
                               terminated by signal.
mandos-monitor: Update to reflect DBus API change.
(MandosClientWidget.checker_completed): Take "signal" instead of
                                        "condition" argument.  Use it
                                        accordingly.  Remove dead code
                                        (os.WCOREDUMP case).

Show diffs side-by-side

added added

removed removed

Lines of Context:
9
9
 * "browse_callback", and parts of "main".
10
10
 * 
11
11
 * Everything else is
12
 
 * Copyright © 2008,2009 Teddy Hogeborn
13
 
 * Copyright © 2008,2009 Björn Påhlsson
 
12
 * Copyright © 2008-2014 Teddy Hogeborn
 
13
 * Copyright © 2008-2014 Björn Påhlsson
14
14
 * 
15
15
 * This program is free software: you can redistribute it and/or
16
16
 * modify it under the terms of the GNU General Public License as
26
26
 * along with this program.  If not, see
27
27
 * <http://www.gnu.org/licenses/>.
28
28
 * 
29
 
 * Contact the authors at <mandos@fukt.bsnet.se>.
 
29
 * Contact the authors at <mandos@recompile.se>.
30
30
 */
31
31
 
32
32
/* Needed by GPGME, specifically gpgme_data_seek() */
33
33
#ifndef _LARGEFILE_SOURCE
34
34
#define _LARGEFILE_SOURCE
35
 
#endif
 
35
#endif  /* not _LARGEFILE_SOURCE */
36
36
#ifndef _FILE_OFFSET_BITS
37
37
#define _FILE_OFFSET_BITS 64
38
 
#endif
 
38
#endif  /* not _FILE_OFFSET_BITS */
39
39
 
40
40
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY(), asprintf() */
41
41
 
42
42
#include <stdio.h>              /* fprintf(), stderr, fwrite(),
43
 
                                   stdout, ferror(), remove() */
44
 
#include <stdint.h>             /* uint16_t, uint32_t */
 
43
                                   stdout, ferror() */
 
44
#include <stdint.h>             /* uint16_t, uint32_t, intptr_t */
45
45
#include <stddef.h>             /* NULL, size_t, ssize_t */
46
 
#include <stdlib.h>             /* free(), EXIT_SUCCESS, EXIT_FAILURE,
47
 
                                   srand(), strtof() */
 
46
#include <stdlib.h>             /* free(), EXIT_SUCCESS, srand(),
 
47
                                   strtof(), abort() */
48
48
#include <stdbool.h>            /* bool, false, true */
49
49
#include <string.h>             /* memset(), strcmp(), strlen(),
50
50
                                   strerror(), asprintf(), strcpy() */
53
53
                                   sockaddr_in6, PF_INET6,
54
54
                                   SOCK_STREAM, uid_t, gid_t, open(),
55
55
                                   opendir(), DIR */
56
 
#include <sys/stat.h>           /* open() */
 
56
#include <sys/stat.h>           /* open(), S_ISREG */
57
57
#include <sys/socket.h>         /* socket(), struct sockaddr_in6,
58
 
                                   inet_pton(), connect() */
59
 
#include <fcntl.h>              /* open() */
 
58
                                   inet_pton(), connect(),
 
59
                                   getnameinfo() */
 
60
#include <fcntl.h>              /* open(), unlinkat() */
60
61
#include <dirent.h>             /* opendir(), struct dirent, readdir()
61
62
                                 */
62
63
#include <inttypes.h>           /* PRIu16, PRIdMAX, intmax_t,
63
64
                                   strtoimax() */
64
 
#include <assert.h>             /* assert() */
65
 
#include <errno.h>              /* perror(), errno */
66
 
#include <time.h>               /* nanosleep(), time() */
 
65
#include <errno.h>              /* perror(), errno,
 
66
                                   program_invocation_short_name */
 
67
#include <time.h>               /* nanosleep(), time(), sleep() */
67
68
#include <net/if.h>             /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
68
69
                                   SIOCSIFFLAGS, if_indextoname(),
69
70
                                   if_nametoindex(), IF_NAMESIZE */
71
72
                                   INET_ADDRSTRLEN, INET6_ADDRSTRLEN
72
73
                                */
73
74
#include <unistd.h>             /* close(), SEEK_SET, off_t, write(),
74
 
                                   getuid(), getgid(), setuid(),
75
 
                                   setgid() */
76
 
#include <arpa/inet.h>          /* inet_pton(), htons */
 
75
                                   getuid(), getgid(), seteuid(),
 
76
                                   setgid(), pause(), _exit(),
 
77
                                   unlinkat() */
 
78
#include <arpa/inet.h>          /* inet_pton(), htons() */
77
79
#include <iso646.h>             /* not, or, and */
78
80
#include <argp.h>               /* struct argp_option, error_t, struct
79
81
                                   argp_state, struct argp,
80
82
                                   argp_parse(), ARGP_KEY_ARG,
81
83
                                   ARGP_KEY_END, ARGP_ERR_UNKNOWN */
82
84
#include <signal.h>             /* sigemptyset(), sigaddset(),
83
 
                                   sigaction(), SIGTERM, sigaction,
84
 
                                   sig_atomic_t */
 
85
                                   sigaction(), SIGTERM, sig_atomic_t,
 
86
                                   raise() */
 
87
#include <sysexits.h>           /* EX_OSERR, EX_USAGE, EX_UNAVAILABLE,
 
88
                                   EX_NOHOST, EX_IOERR, EX_PROTOCOL */
 
89
#include <sys/wait.h>           /* waitpid(), WIFEXITED(),
 
90
                                   WEXITSTATUS(), WTERMSIG() */
 
91
#include <grp.h>                /* setgroups() */
 
92
#include <argz.h>               /* argz_add_sep(), argz_next(),
 
93
                                   argz_delete(), argz_append(),
 
94
                                   argz_stringify(), argz_add(),
 
95
                                   argz_count() */
 
96
#include <netdb.h>              /* getnameinfo(), NI_NUMERICHOST,
 
97
                                   EAI_SYSTEM, gai_strerror() */
85
98
 
86
99
#ifdef __linux__
87
100
#include <sys/klog.h>           /* klogctl() */
105
118
                                   init_gnutls_session(),
106
119
                                   GNUTLS_* */
107
120
#include <gnutls/openpgp.h>
108
 
                          /* gnutls_certificate_set_openpgp_key_file(),
109
 
                                   GNUTLS_OPENPGP_FMT_BASE64 */
 
121
                         /* gnutls_certificate_set_openpgp_key_file(),
 
122
                            GNUTLS_OPENPGP_FMT_BASE64 */
110
123
 
111
124
/* GPGME */
112
125
#include <gpgme.h>              /* All GPGME types, constants and
120
133
#define PATHDIR "/conf/conf.d/mandos"
121
134
#define SECKEY "seckey.txt"
122
135
#define PUBKEY "pubkey.txt"
 
136
#define HOOKDIR "/lib/mandos/network-hooks.d"
123
137
 
124
138
bool debug = false;
125
139
static const char mandos_protocol_version[] = "1";
126
140
const char *argp_program_version = "mandos-client " VERSION;
127
 
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
 
141
const char *argp_program_bug_address = "<mandos@recompile.se>";
 
142
static const char sys_class_net[] = "/sys/class/net";
 
143
char *connect_to = NULL;
 
144
const char *hookdir = HOOKDIR;
 
145
int hookdir_fd = -1;
 
146
uid_t uid = 65534;
 
147
gid_t gid = 65534;
 
148
 
 
149
/* Doubly linked list that need to be circularly linked when used */
 
150
typedef struct server{
 
151
  const char *ip;
 
152
  in_port_t port;
 
153
  AvahiIfIndex if_index;
 
154
  int af;
 
155
  struct timespec last_seen;
 
156
  struct server *next;
 
157
  struct server *prev;
 
158
} server;
128
159
 
129
160
/* Used for passing in values through the Avahi callback functions */
130
161
typedef struct {
131
 
  AvahiSimplePoll *simple_poll;
132
162
  AvahiServer *server;
133
163
  gnutls_certificate_credentials_t cred;
134
164
  unsigned int dh_bits;
135
165
  gnutls_dh_params_t dh_params;
136
166
  const char *priority;
137
167
  gpgme_ctx_t ctx;
 
168
  server *current_server;
 
169
  char *interfaces;
 
170
  size_t interfaces_size;
138
171
} mandos_context;
139
172
 
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" };
 
173
/* global so signal handler can reach it*/
 
174
AvahiSimplePoll *simple_poll;
 
175
 
 
176
sig_atomic_t quit_now = 0;
 
177
int signal_received = 0;
 
178
 
 
179
/* Function to use when printing errors */
 
180
void perror_plus(const char *print_text){
 
181
  int e = errno;
 
182
  fprintf(stderr, "Mandos plugin %s: ",
 
183
          program_invocation_short_name);
 
184
  errno = e;
 
185
  perror(print_text);
 
186
}
 
187
 
 
188
__attribute__((format (gnu_printf, 2, 3), nonnull))
 
189
int fprintf_plus(FILE *stream, const char *format, ...){
 
190
  va_list ap;
 
191
  va_start (ap, format);
 
192
  
 
193
  TEMP_FAILURE_RETRY(fprintf(stream, "Mandos plugin %s: ",
 
194
                             program_invocation_short_name));
 
195
  return (int)TEMP_FAILURE_RETRY(vfprintf(stream, format, ap));
 
196
}
144
197
 
145
198
/*
146
199
 * Make additional room in "buffer" for at least BUFFER_SIZE more
147
200
 * bytes. "buffer_capacity" is how much is currently allocated,
148
201
 * "buffer_length" is how much is already used.
149
202
 */
 
203
__attribute__((nonnull, warn_unused_result))
150
204
size_t incbuffer(char **buffer, size_t buffer_length,
151
 
                  size_t buffer_capacity){
 
205
                 size_t buffer_capacity){
152
206
  if(buffer_length + BUFFER_SIZE > buffer_capacity){
153
 
    *buffer = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
154
 
    if(buffer == NULL){
 
207
    char *new_buf = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
 
208
    if(new_buf == NULL){
 
209
      int old_errno = errno;
 
210
      free(*buffer);
 
211
      errno = old_errno;
 
212
      *buffer = NULL;
155
213
      return 0;
156
214
    }
 
215
    *buffer = new_buf;
157
216
    buffer_capacity += BUFFER_SIZE;
158
217
  }
159
218
  return buffer_capacity;
160
219
}
161
220
 
 
221
/* Add server to set of servers to retry periodically */
 
222
__attribute__((nonnull, warn_unused_result))
 
223
bool add_server(const char *ip, in_port_t port, AvahiIfIndex if_index,
 
224
                int af, server **current_server){
 
225
  int ret;
 
226
  server *new_server = malloc(sizeof(server));
 
227
  if(new_server == NULL){
 
228
    perror_plus("malloc");
 
229
    return false;
 
230
  }
 
231
  *new_server = (server){ .ip = strdup(ip),
 
232
                          .port = port,
 
233
                          .if_index = if_index,
 
234
                          .af = af };
 
235
  if(new_server->ip == NULL){
 
236
    perror_plus("strdup");
 
237
    free(new_server);
 
238
    return false;
 
239
  }
 
240
  ret = clock_gettime(CLOCK_MONOTONIC, &(new_server->last_seen));
 
241
  if(ret == -1){
 
242
    perror_plus("clock_gettime");
 
243
#ifdef __GNUC__
 
244
#pragma GCC diagnostic push
 
245
#pragma GCC diagnostic ignored "-Wcast-qual"
 
246
#endif
 
247
    free((char *)(new_server->ip));
 
248
#ifdef __GNUC__
 
249
#pragma GCC diagnostic pop
 
250
#endif
 
251
    free(new_server);
 
252
    return false;
 
253
  }
 
254
  /* Special case of first server */
 
255
  if(*current_server == NULL){
 
256
    new_server->next = new_server;
 
257
    new_server->prev = new_server;
 
258
    *current_server = new_server;
 
259
  } else {
 
260
    /* Place the new server last in the list */
 
261
    new_server->next = *current_server;
 
262
    new_server->prev = (*current_server)->prev;
 
263
    new_server->prev->next = new_server;
 
264
    (*current_server)->prev = new_server;
 
265
  }
 
266
  return true;
 
267
}
 
268
 
162
269
/* 
163
270
 * Initialize GPGME.
164
271
 */
165
 
static bool init_gpgme(const char *seckey,
166
 
                       const char *pubkey, const char *tempdir){
167
 
  int ret;
 
272
__attribute__((nonnull, warn_unused_result))
 
273
static bool init_gpgme(const char * const seckey,
 
274
                       const char * const pubkey,
 
275
                       const char * const tempdir,
 
276
                       mandos_context *mc){
168
277
  gpgme_error_t rc;
169
278
  gpgme_engine_info_t engine_info;
170
279
  
171
 
  
172
280
  /*
173
281
   * Helper function to insert pub and seckey to the engine keyring.
174
282
   */
175
 
  bool import_key(const char *filename){
 
283
  bool import_key(const char * const filename){
 
284
    int ret;
176
285
    int fd;
177
286
    gpgme_data_t pgp_data;
178
287
    
179
288
    fd = (int)TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
180
289
    if(fd == -1){
181
 
      perror("open");
 
290
      perror_plus("open");
182
291
      return false;
183
292
    }
184
293
    
185
294
    rc = gpgme_data_new_from_fd(&pgp_data, fd);
186
295
    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));
 
296
      fprintf_plus(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
 
297
                   gpgme_strsource(rc), gpgme_strerror(rc));
189
298
      return false;
190
299
    }
191
300
    
192
 
    rc = gpgme_op_import(mc.ctx, pgp_data);
 
301
    rc = gpgme_op_import(mc->ctx, pgp_data);
193
302
    if(rc != GPG_ERR_NO_ERROR){
194
 
      fprintf(stderr, "bad gpgme_op_import: %s: %s\n",
195
 
              gpgme_strsource(rc), gpgme_strerror(rc));
 
303
      fprintf_plus(stderr, "bad gpgme_op_import: %s: %s\n",
 
304
                   gpgme_strsource(rc), gpgme_strerror(rc));
196
305
      return false;
197
306
    }
198
307
    
199
308
    ret = (int)TEMP_FAILURE_RETRY(close(fd));
200
309
    if(ret == -1){
201
 
      perror("close");
 
310
      perror_plus("close");
202
311
    }
203
312
    gpgme_data_release(pgp_data);
204
313
    return true;
205
314
  }
206
315
  
207
316
  if(debug){
208
 
    fprintf(stderr, "Initializing GPGME\n");
 
317
    fprintf_plus(stderr, "Initializing GPGME\n");
209
318
  }
210
319
  
211
320
  /* Init GPGME */
212
321
  gpgme_check_version(NULL);
213
322
  rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
214
323
  if(rc != GPG_ERR_NO_ERROR){
215
 
    fprintf(stderr, "bad gpgme_engine_check_version: %s: %s\n",
216
 
            gpgme_strsource(rc), gpgme_strerror(rc));
 
324
    fprintf_plus(stderr, "bad gpgme_engine_check_version: %s: %s\n",
 
325
                 gpgme_strsource(rc), gpgme_strerror(rc));
217
326
    return false;
218
327
  }
219
328
  
220
 
    /* Set GPGME home directory for the OpenPGP engine only */
 
329
  /* Set GPGME home directory for the OpenPGP engine only */
221
330
  rc = gpgme_get_engine_info(&engine_info);
222
331
  if(rc != GPG_ERR_NO_ERROR){
223
 
    fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
224
 
            gpgme_strsource(rc), gpgme_strerror(rc));
 
332
    fprintf_plus(stderr, "bad gpgme_get_engine_info: %s: %s\n",
 
333
                 gpgme_strsource(rc), gpgme_strerror(rc));
225
334
    return false;
226
335
  }
227
336
  while(engine_info != NULL){
233
342
    engine_info = engine_info->next;
234
343
  }
235
344
  if(engine_info == NULL){
236
 
    fprintf(stderr, "Could not set GPGME home dir to %s\n", tempdir);
 
345
    fprintf_plus(stderr, "Could not set GPGME home dir to %s\n",
 
346
                 tempdir);
237
347
    return false;
238
348
  }
239
349
  
240
350
  /* Create new GPGME "context" */
241
 
  rc = gpgme_new(&(mc.ctx));
 
351
  rc = gpgme_new(&(mc->ctx));
242
352
  if(rc != GPG_ERR_NO_ERROR){
243
 
    fprintf(stderr, "bad gpgme_new: %s: %s\n",
244
 
            gpgme_strsource(rc), gpgme_strerror(rc));
 
353
    fprintf_plus(stderr, "Mandos plugin mandos-client: "
 
354
                 "bad gpgme_new: %s: %s\n", gpgme_strsource(rc),
 
355
                 gpgme_strerror(rc));
245
356
    return false;
246
357
  }
247
358
  
249
360
    return false;
250
361
  }
251
362
  
252
 
  return true; 
 
363
  return true;
253
364
}
254
365
 
255
366
/* 
256
367
 * Decrypt OpenPGP data.
257
368
 * Returns -1 on error
258
369
 */
 
370
__attribute__((nonnull, warn_unused_result))
259
371
static ssize_t pgp_packet_decrypt(const char *cryptotext,
260
372
                                  size_t crypto_size,
261
 
                                  char **plaintext){
 
373
                                  char **plaintext,
 
374
                                  mandos_context *mc){
262
375
  gpgme_data_t dh_crypto, dh_plain;
263
376
  gpgme_error_t rc;
264
377
  ssize_t ret;
266
379
  ssize_t plaintext_length = 0;
267
380
  
268
381
  if(debug){
269
 
    fprintf(stderr, "Trying to decrypt OpenPGP data\n");
 
382
    fprintf_plus(stderr, "Trying to decrypt OpenPGP data\n");
270
383
  }
271
384
  
272
385
  /* Create new GPGME data buffer from memory cryptotext */
273
386
  rc = gpgme_data_new_from_mem(&dh_crypto, cryptotext, crypto_size,
274
387
                               0);
275
388
  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));
 
389
    fprintf_plus(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
 
390
                 gpgme_strsource(rc), gpgme_strerror(rc));
278
391
    return -1;
279
392
  }
280
393
  
281
394
  /* Create new empty GPGME data buffer for the plaintext */
282
395
  rc = gpgme_data_new(&dh_plain);
283
396
  if(rc != GPG_ERR_NO_ERROR){
284
 
    fprintf(stderr, "bad gpgme_data_new: %s: %s\n",
285
 
            gpgme_strsource(rc), gpgme_strerror(rc));
 
397
    fprintf_plus(stderr, "Mandos plugin mandos-client: "
 
398
                 "bad gpgme_data_new: %s: %s\n",
 
399
                 gpgme_strsource(rc), gpgme_strerror(rc));
286
400
    gpgme_data_release(dh_crypto);
287
401
    return -1;
288
402
  }
289
403
  
290
404
  /* Decrypt data from the cryptotext data buffer to the plaintext
291
405
     data buffer */
292
 
  rc = gpgme_op_decrypt(mc.ctx, dh_crypto, dh_plain);
 
406
  rc = gpgme_op_decrypt(mc->ctx, dh_crypto, dh_plain);
293
407
  if(rc != GPG_ERR_NO_ERROR){
294
 
    fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
295
 
            gpgme_strsource(rc), gpgme_strerror(rc));
 
408
    fprintf_plus(stderr, "bad gpgme_op_decrypt: %s: %s\n",
 
409
                 gpgme_strsource(rc), gpgme_strerror(rc));
296
410
    plaintext_length = -1;
297
411
    if(debug){
298
412
      gpgme_decrypt_result_t result;
299
 
      result = gpgme_op_decrypt_result(mc.ctx);
 
413
      result = gpgme_op_decrypt_result(mc->ctx);
300
414
      if(result == NULL){
301
 
        fprintf(stderr, "gpgme_op_decrypt_result failed\n");
 
415
        fprintf_plus(stderr, "gpgme_op_decrypt_result failed\n");
302
416
      } 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);
 
417
        fprintf_plus(stderr, "Unsupported algorithm: %s\n",
 
418
                     result->unsupported_algorithm);
 
419
        fprintf_plus(stderr, "Wrong key usage: %u\n",
 
420
                     result->wrong_key_usage);
307
421
        if(result->file_name != NULL){
308
 
          fprintf(stderr, "File name: %s\n", result->file_name);
 
422
          fprintf_plus(stderr, "File name: %s\n", result->file_name);
309
423
        }
310
424
        gpgme_recipient_t recipient;
311
425
        recipient = result->recipients;
312
426
        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");
 
427
          fprintf_plus(stderr, "Public key algorithm: %s\n",
 
428
                       gpgme_pubkey_algo_name
 
429
                       (recipient->pubkey_algo));
 
430
          fprintf_plus(stderr, "Key ID: %s\n", recipient->keyid);
 
431
          fprintf_plus(stderr, "Secret key available: %s\n",
 
432
                       recipient->status == GPG_ERR_NO_SECKEY
 
433
                       ? "No" : "Yes");
319
434
          recipient = recipient->next;
320
435
        }
321
436
      }
324
439
  }
325
440
  
326
441
  if(debug){
327
 
    fprintf(stderr, "Decryption of OpenPGP data succeeded\n");
 
442
    fprintf_plus(stderr, "Decryption of OpenPGP data succeeded\n");
328
443
  }
329
444
  
330
445
  /* Seek back to the beginning of the GPGME plaintext data buffer */
331
446
  if(gpgme_data_seek(dh_plain, (off_t)0, SEEK_SET) == -1){
332
 
    perror("gpgme_data_seek");
 
447
    perror_plus("gpgme_data_seek");
333
448
    plaintext_length = -1;
334
449
    goto decrypt_end;
335
450
  }
337
452
  *plaintext = NULL;
338
453
  while(true){
339
454
    plaintext_capacity = incbuffer(plaintext,
340
 
                                      (size_t)plaintext_length,
341
 
                                      plaintext_capacity);
 
455
                                   (size_t)plaintext_length,
 
456
                                   plaintext_capacity);
342
457
    if(plaintext_capacity == 0){
343
 
        perror("incbuffer");
344
 
        plaintext_length = -1;
345
 
        goto decrypt_end;
 
458
      perror_plus("incbuffer");
 
459
      plaintext_length = -1;
 
460
      goto decrypt_end;
346
461
    }
347
462
    
348
463
    ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
353
468
      break;
354
469
    }
355
470
    if(ret < 0){
356
 
      perror("gpgme_data_read");
 
471
      perror_plus("gpgme_data_read");
357
472
      plaintext_length = -1;
358
473
      goto decrypt_end;
359
474
    }
361
476
  }
362
477
  
363
478
  if(debug){
364
 
    fprintf(stderr, "Decrypted password is: ");
 
479
    fprintf_plus(stderr, "Decrypted password is: ");
365
480
    for(ssize_t i = 0; i < plaintext_length; i++){
366
481
      fprintf(stderr, "%02hhX ", (*plaintext)[i]);
367
482
    }
378
493
  return plaintext_length;
379
494
}
380
495
 
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;
 
496
__attribute__((warn_unused_result, const))
 
497
static const char *safe_string(const char *str){
 
498
  if(str == NULL)
 
499
    return "(unknown)";
 
500
  return str;
 
501
}
 
502
 
 
503
__attribute__((warn_unused_result))
 
504
static const char *safer_gnutls_strerror(int value){
 
505
  const char *ret = gnutls_strerror(value);
 
506
  return safe_string(ret);
387
507
}
388
508
 
389
509
/* GnuTLS log function callback */
 
510
__attribute__((nonnull))
390
511
static void debuggnutls(__attribute__((unused)) int level,
391
512
                        const char* string){
392
 
  fprintf(stderr, "GnuTLS: %s", string);
 
513
  fprintf_plus(stderr, "GnuTLS: %s", string);
393
514
}
394
515
 
 
516
__attribute__((nonnull, warn_unused_result))
395
517
static int init_gnutls_global(const char *pubkeyfilename,
396
 
                              const char *seckeyfilename){
 
518
                              const char *seckeyfilename,
 
519
                              mandos_context *mc){
397
520
  int ret;
 
521
  unsigned int uret;
398
522
  
399
523
  if(debug){
400
 
    fprintf(stderr, "Initializing GnuTLS\n");
 
524
    fprintf_plus(stderr, "Initializing GnuTLS\n");
401
525
  }
402
526
  
403
527
  ret = gnutls_global_init();
404
528
  if(ret != GNUTLS_E_SUCCESS){
405
 
    fprintf(stderr, "GnuTLS global_init: %s\n",
406
 
            safer_gnutls_strerror(ret));
 
529
    fprintf_plus(stderr, "GnuTLS global_init: %s\n",
 
530
                 safer_gnutls_strerror(ret));
407
531
    return -1;
408
532
  }
409
533
  
416
540
  }
417
541
  
418
542
  /* OpenPGP credentials */
419
 
  gnutls_certificate_allocate_credentials(&mc.cred);
 
543
  ret = gnutls_certificate_allocate_credentials(&mc->cred);
420
544
  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));
 
545
    fprintf_plus(stderr, "GnuTLS memory error: %s\n",
 
546
                 safer_gnutls_strerror(ret));
426
547
    gnutls_global_deinit();
427
548
    return -1;
428
549
  }
429
550
  
430
551
  if(debug){
431
 
    fprintf(stderr, "Attempting to use OpenPGP public key %s and"
432
 
            " secret key %s as GnuTLS credentials\n", pubkeyfilename,
433
 
            seckeyfilename);
 
552
    fprintf_plus(stderr, "Attempting to use OpenPGP public key %s and"
 
553
                 " secret key %s as GnuTLS credentials\n",
 
554
                 pubkeyfilename,
 
555
                 seckeyfilename);
434
556
  }
435
557
  
436
558
  ret = gnutls_certificate_set_openpgp_key_file
437
 
    (mc.cred, pubkeyfilename, seckeyfilename,
 
559
    (mc->cred, pubkeyfilename, seckeyfilename,
438
560
     GNUTLS_OPENPGP_FMT_BASE64);
439
561
  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));
 
562
    fprintf_plus(stderr,
 
563
                 "Error[%d] while reading the OpenPGP key pair ('%s',"
 
564
                 " '%s')\n", ret, pubkeyfilename, seckeyfilename);
 
565
    fprintf_plus(stderr, "The GnuTLS error is: %s\n",
 
566
                 safer_gnutls_strerror(ret));
445
567
    goto globalfail;
446
568
  }
447
569
  
448
570
  /* GnuTLS server initialization */
449
 
  ret = gnutls_dh_params_init(&mc.dh_params);
 
571
  ret = gnutls_dh_params_init(&mc->dh_params);
450
572
  if(ret != GNUTLS_E_SUCCESS){
451
 
    fprintf(stderr, "Error in GnuTLS DH parameter initialization:"
452
 
            " %s\n", safer_gnutls_strerror(ret));
 
573
    fprintf_plus(stderr, "Error in GnuTLS DH parameter"
 
574
                 " initialization: %s\n",
 
575
                 safer_gnutls_strerror(ret));
453
576
    goto globalfail;
454
577
  }
455
 
  ret = gnutls_dh_params_generate2(mc.dh_params, mc.dh_bits);
 
578
  if(mc->dh_bits == 0){
 
579
    /* Find out the optimal number of DH bits */
 
580
    /* Try to read the private key file */
 
581
    gnutls_datum_t buffer = { .data = NULL, .size = 0 };
 
582
    {
 
583
      int secfile = open(seckeyfilename, O_RDONLY);
 
584
      size_t buffer_capacity = 0;
 
585
      while(true){
 
586
        buffer_capacity = incbuffer((char **)&buffer.data,
 
587
                                    (size_t)buffer.size,
 
588
                                    (size_t)buffer_capacity);
 
589
        if(buffer_capacity == 0){
 
590
          perror_plus("incbuffer");
 
591
          free(buffer.data);
 
592
          buffer.data = NULL;
 
593
          break;
 
594
        }
 
595
        ssize_t bytes_read = read(secfile, buffer.data + buffer.size,
 
596
                                  BUFFER_SIZE);
 
597
        /* EOF */
 
598
        if(bytes_read == 0){
 
599
          break;
 
600
        }
 
601
        /* check bytes_read for failure */
 
602
        if(bytes_read < 0){
 
603
          perror_plus("read");
 
604
          free(buffer.data);
 
605
          buffer.data = NULL;
 
606
          break;
 
607
        }
 
608
        buffer.size += (unsigned int)bytes_read;
 
609
      }
 
610
      close(secfile);
 
611
    }
 
612
    /* If successful, use buffer to parse private key */
 
613
    gnutls_sec_param_t sec_param = GNUTLS_SEC_PARAM_ULTRA;
 
614
    if(buffer.data != NULL){
 
615
      {
 
616
        gnutls_openpgp_privkey_t privkey = NULL;
 
617
        ret = gnutls_openpgp_privkey_init(&privkey);
 
618
        if(ret != GNUTLS_E_SUCCESS){
 
619
          fprintf_plus(stderr, "Error initializing OpenPGP key"
 
620
                       " structure: %s", safer_gnutls_strerror(ret));
 
621
          free(buffer.data);
 
622
          buffer.data = NULL;
 
623
        } else {
 
624
          ret = gnutls_openpgp_privkey_import(privkey, &buffer,
 
625
                                            GNUTLS_OPENPGP_FMT_BASE64,
 
626
                                              "", 0);
 
627
          if(ret != GNUTLS_E_SUCCESS){
 
628
            fprintf_plus(stderr, "Error importing OpenPGP key : %s",
 
629
                         safer_gnutls_strerror(ret));
 
630
            privkey = NULL;
 
631
          }
 
632
          free(buffer.data);
 
633
          buffer.data = NULL;
 
634
          if(privkey != NULL){
 
635
            /* Use private key to suggest an appropriate sec_param */
 
636
            sec_param = gnutls_openpgp_privkey_sec_param(privkey);
 
637
            gnutls_openpgp_privkey_deinit(privkey);
 
638
            if(debug){
 
639
              fprintf_plus(stderr, "This OpenPGP key implies using a"
 
640
                           " GnuTLS security parameter \"%s\".\n",
 
641
                           safe_string(gnutls_sec_param_get_name
 
642
                                       (sec_param)));
 
643
            }
 
644
          }
 
645
        }
 
646
      }
 
647
      if(sec_param == GNUTLS_SEC_PARAM_UNKNOWN){
 
648
        /* Err on the side of caution */
 
649
        sec_param = GNUTLS_SEC_PARAM_ULTRA;
 
650
        if(debug){
 
651
          fprintf_plus(stderr, "Falling back to security parameter"
 
652
                       " \"%s\"\n",
 
653
                       safe_string(gnutls_sec_param_get_name
 
654
                                   (sec_param)));
 
655
        }
 
656
      }
 
657
    }
 
658
    uret = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, sec_param);
 
659
    if(uret != 0){
 
660
      mc->dh_bits = uret;
 
661
      if(debug){
 
662
        fprintf_plus(stderr, "A \"%s\" GnuTLS security parameter"
 
663
                     " implies %u DH bits; using that.\n",
 
664
                     safe_string(gnutls_sec_param_get_name
 
665
                                 (sec_param)),
 
666
                     mc->dh_bits);
 
667
      }
 
668
    } else {
 
669
      fprintf_plus(stderr, "Failed to get implied number of DH"
 
670
                   " bits for security parameter \"%s\"): %s\n",
 
671
                   safe_string(gnutls_sec_param_get_name(sec_param)),
 
672
                   safer_gnutls_strerror(ret));
 
673
      goto globalfail;
 
674
    }
 
675
  } else if(debug){
 
676
    fprintf_plus(stderr, "DH bits explicitly set to %u\n",
 
677
                 mc->dh_bits);
 
678
  }
 
679
  ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
456
680
  if(ret != GNUTLS_E_SUCCESS){
457
 
    fprintf(stderr, "Error in GnuTLS prime generation: %s\n",
458
 
            safer_gnutls_strerror(ret));
 
681
    fprintf_plus(stderr, "Error in GnuTLS prime generation (%u bits):"
 
682
                 " %s\n", mc->dh_bits, safer_gnutls_strerror(ret));
459
683
    goto globalfail;
460
684
  }
461
685
  
462
 
  gnutls_certificate_set_dh_params(mc.cred, mc.dh_params);
 
686
  gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
463
687
  
464
688
  return 0;
465
689
  
466
690
 globalfail:
467
691
  
468
 
  gnutls_certificate_free_credentials(mc.cred);
 
692
  gnutls_certificate_free_credentials(mc->cred);
469
693
  gnutls_global_deinit();
470
 
  gnutls_dh_params_deinit(mc.dh_params);
 
694
  gnutls_dh_params_deinit(mc->dh_params);
471
695
  return -1;
472
696
}
473
697
 
474
 
static int init_gnutls_session(gnutls_session_t *session){
 
698
__attribute__((nonnull, warn_unused_result))
 
699
static int init_gnutls_session(gnutls_session_t *session,
 
700
                               mandos_context *mc){
475
701
  int ret;
476
702
  /* GnuTLS session creation */
477
 
  ret = gnutls_init(session, GNUTLS_SERVER);
 
703
  do {
 
704
    ret = gnutls_init(session, GNUTLS_SERVER);
 
705
    if(quit_now){
 
706
      return -1;
 
707
    }
 
708
  } while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
478
709
  if(ret != GNUTLS_E_SUCCESS){
479
 
    fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
480
 
            safer_gnutls_strerror(ret));
 
710
    fprintf_plus(stderr,
 
711
                 "Error in GnuTLS session initialization: %s\n",
 
712
                 safer_gnutls_strerror(ret));
481
713
  }
482
714
  
483
715
  {
484
716
    const char *err;
485
 
    ret = gnutls_priority_set_direct(*session, mc.priority, &err);
 
717
    do {
 
718
      ret = gnutls_priority_set_direct(*session, mc->priority, &err);
 
719
      if(quit_now){
 
720
        gnutls_deinit(*session);
 
721
        return -1;
 
722
      }
 
723
    } while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
486
724
    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));
 
725
      fprintf_plus(stderr, "Syntax error at: %s\n", err);
 
726
      fprintf_plus(stderr, "GnuTLS error: %s\n",
 
727
                   safer_gnutls_strerror(ret));
490
728
      gnutls_deinit(*session);
491
729
      return -1;
492
730
    }
493
731
  }
494
732
  
495
 
  ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
496
 
                               mc.cred);
 
733
  do {
 
734
    ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
 
735
                                 mc->cred);
 
736
    if(quit_now){
 
737
      gnutls_deinit(*session);
 
738
      return -1;
 
739
    }
 
740
  } while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
497
741
  if(ret != GNUTLS_E_SUCCESS){
498
 
    fprintf(stderr, "Error setting GnuTLS credentials: %s\n",
499
 
            safer_gnutls_strerror(ret));
 
742
    fprintf_plus(stderr, "Error setting GnuTLS credentials: %s\n",
 
743
                 safer_gnutls_strerror(ret));
500
744
    gnutls_deinit(*session);
501
745
    return -1;
502
746
  }
503
747
  
504
748
  /* ignore client certificate if any. */
505
 
  gnutls_certificate_server_set_request(*session,
506
 
                                        GNUTLS_CERT_IGNORE);
 
749
  gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE);
507
750
  
508
 
  gnutls_dh_set_prime_bits(*session, mc.dh_bits);
 
751
  gnutls_dh_set_prime_bits(*session, mc->dh_bits);
509
752
  
510
753
  return 0;
511
754
}
515
758
                      __attribute__((unused)) const char *txt){}
516
759
 
517
760
/* Called when a Mandos server is found */
518
 
static int start_mandos_communication(const char *ip, uint16_t port,
 
761
__attribute__((nonnull, warn_unused_result))
 
762
static int start_mandos_communication(const char *ip, in_port_t port,
519
763
                                      AvahiIfIndex if_index,
520
 
                                      int af){
521
 
  int ret, tcp_sd;
 
764
                                      int af, mandos_context *mc){
 
765
  int ret, tcp_sd = -1;
522
766
  ssize_t sret;
523
 
  union {
524
 
    struct sockaddr_in in;
525
 
    struct sockaddr_in6 in6;
526
 
  } to;
 
767
  struct sockaddr_storage to;
527
768
  char *buffer = NULL;
528
 
  char *decrypted_buffer;
 
769
  char *decrypted_buffer = NULL;
529
770
  size_t buffer_length = 0;
530
771
  size_t buffer_capacity = 0;
531
 
  ssize_t decrypted_buffer_size;
532
772
  size_t written;
533
 
  int retval = 0;
 
773
  int retval = -1;
534
774
  gnutls_session_t session;
535
775
  int pf;                       /* Protocol family */
536
776
  
 
777
  errno = 0;
 
778
  
 
779
  if(quit_now){
 
780
    errno = EINTR;
 
781
    return -1;
 
782
  }
 
783
  
537
784
  switch(af){
538
785
  case AF_INET6:
539
786
    pf = PF_INET6;
542
789
    pf = PF_INET;
543
790
    break;
544
791
  default:
545
 
    fprintf(stderr, "Bad address family: %d\n", af);
 
792
    fprintf_plus(stderr, "Bad address family: %d\n", af);
 
793
    errno = EINVAL;
546
794
    return -1;
547
795
  }
548
796
  
549
 
  ret = init_gnutls_session(&session);
 
797
  /* If the interface is specified and we have a list of interfaces */
 
798
  if(if_index != AVAHI_IF_UNSPEC and mc->interfaces != NULL){
 
799
    /* Check if the interface is one of the interfaces we are using */
 
800
    bool match = false;
 
801
    {
 
802
      char *interface = NULL;
 
803
      while((interface=argz_next(mc->interfaces, mc->interfaces_size,
 
804
                                 interface))){
 
805
        if(if_nametoindex(interface) == (unsigned int)if_index){
 
806
          match = true;
 
807
          break;
 
808
        }
 
809
      }
 
810
    }
 
811
    if(not match){
 
812
      /* This interface does not match any in the list, so we don't
 
813
         connect to the server */
 
814
      if(debug){
 
815
        char interface[IF_NAMESIZE];
 
816
        if(if_indextoname((unsigned int)if_index, interface) == NULL){
 
817
          perror_plus("if_indextoname");
 
818
        } else {
 
819
          fprintf_plus(stderr, "Skipping server on non-used interface"
 
820
                       " \"%s\"\n",
 
821
                       if_indextoname((unsigned int)if_index,
 
822
                                      interface));
 
823
        }
 
824
      }
 
825
      return -1;
 
826
    }
 
827
  }
 
828
  
 
829
  ret = init_gnutls_session(&session, mc);
550
830
  if(ret != 0){
551
831
    return -1;
552
832
  }
553
833
  
554
834
  if(debug){
555
 
    fprintf(stderr, "Setting up a TCP connection to %s, port %" PRIu16
556
 
            "\n", ip, port);
 
835
    fprintf_plus(stderr, "Setting up a TCP connection to %s, port %"
 
836
                 PRIuMAX "\n", ip, (uintmax_t)port);
557
837
  }
558
838
  
559
839
  tcp_sd = socket(pf, SOCK_STREAM, 0);
560
840
  if(tcp_sd < 0){
561
 
    perror("socket");
562
 
    return -1;
 
841
    int e = errno;
 
842
    perror_plus("socket");
 
843
    errno = e;
 
844
    goto mandos_end;
 
845
  }
 
846
  
 
847
  if(quit_now){
 
848
    errno = EINTR;
 
849
    goto mandos_end;
563
850
  }
564
851
  
565
852
  memset(&to, 0, sizeof(to));
566
853
  if(af == AF_INET6){
567
 
    to.in6.sin6_family = (sa_family_t)af;
568
 
    ret = inet_pton(af, ip, &to.in6.sin6_addr);
 
854
    ((struct sockaddr_in6 *)&to)->sin6_family = (sa_family_t)af;
 
855
    ret = inet_pton(af, ip, &((struct sockaddr_in6 *)&to)->sin6_addr);
569
856
  } else {                      /* IPv4 */
570
 
    to.in.sin_family = (sa_family_t)af;
571
 
    ret = inet_pton(af, ip, &to.in.sin_addr);
 
857
    ((struct sockaddr_in *)&to)->sin_family = (sa_family_t)af;
 
858
    ret = inet_pton(af, ip, &((struct sockaddr_in *)&to)->sin_addr);
572
859
  }
573
860
  if(ret < 0 ){
574
 
    perror("inet_pton");
575
 
    return -1;
 
861
    int e = errno;
 
862
    perror_plus("inet_pton");
 
863
    errno = e;
 
864
    goto mandos_end;
576
865
  }
577
866
  if(ret == 0){
578
 
    fprintf(stderr, "Bad address: %s\n", ip);
579
 
    return -1;
 
867
    int e = errno;
 
868
    fprintf_plus(stderr, "Bad address: %s\n", ip);
 
869
    errno = e;
 
870
    goto mandos_end;
580
871
  }
581
872
  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*/
 
873
    ((struct sockaddr_in6 *)&to)->sin6_port = htons(port);
 
874
    if(IN6_IS_ADDR_LINKLOCAL
 
875
       (&((struct sockaddr_in6 *)&to)->sin6_addr)){
589
876
      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;
 
877
        fprintf_plus(stderr, "An IPv6 link-local address is"
 
878
                     " incomplete without a network interface\n");
 
879
        errno = EINVAL;
 
880
        goto mandos_end;
593
881
      }
594
882
      /* Set the network interface number as scope */
595
 
      to.in6.sin6_scope_id = (uint32_t)if_index;
 
883
      ((struct sockaddr_in6 *)&to)->sin6_scope_id = (uint32_t)if_index;
596
884
    }
597
885
  } else {
598
 
    to.in.sin_port = htons(port); /* Spurious warnings from
599
 
                                     -Wconversion and
600
 
                                     -Wunreachable-code */
 
886
    ((struct sockaddr_in *)&to)->sin_port = htons(port);
 
887
  }
 
888
  
 
889
  if(quit_now){
 
890
    errno = EINTR;
 
891
    goto mandos_end;
601
892
  }
602
893
  
603
894
  if(debug){
604
895
    if(af == AF_INET6 and if_index != AVAHI_IF_UNSPEC){
605
896
      char interface[IF_NAMESIZE];
606
897
      if(if_indextoname((unsigned int)if_index, interface) == NULL){
607
 
        perror("if_indextoname");
 
898
        perror_plus("if_indextoname");
608
899
      } else {
609
 
        fprintf(stderr, "Connection to: %s%%%s, port %" PRIu16 "\n",
610
 
                ip, interface, port);
 
900
        fprintf_plus(stderr, "Connection to: %s%%%s, port %" PRIuMAX
 
901
                     "\n", ip, interface, (uintmax_t)port);
611
902
      }
612
903
    } else {
613
 
      fprintf(stderr, "Connection to: %s, port %" PRIu16 "\n", ip,
614
 
              port);
 
904
      fprintf_plus(stderr, "Connection to: %s, port %" PRIuMAX "\n",
 
905
                   ip, (uintmax_t)port);
615
906
    }
616
907
    char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ?
617
908
                 INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = "";
618
 
    const char *pcret;
619
909
    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
 
    }
 
910
      ret = getnameinfo((struct sockaddr *)&to,
 
911
                        sizeof(struct sockaddr_in6),
 
912
                        addrstr, sizeof(addrstr), NULL, 0,
 
913
                        NI_NUMERICHOST);
 
914
    } else {
 
915
      ret = getnameinfo((struct sockaddr *)&to,
 
916
                        sizeof(struct sockaddr_in),
 
917
                        addrstr, sizeof(addrstr), NULL, 0,
 
918
                        NI_NUMERICHOST);
 
919
    }
 
920
    if(ret == EAI_SYSTEM){
 
921
      perror_plus("getnameinfo");
 
922
    } else if(ret != 0) {
 
923
      fprintf_plus(stderr, "getnameinfo: %s", gai_strerror(ret));
 
924
    } else if(strcmp(addrstr, ip) != 0){
 
925
      fprintf_plus(stderr, "Canonical address form: %s\n", addrstr);
 
926
    }
 
927
  }
 
928
  
 
929
  if(quit_now){
 
930
    errno = EINTR;
 
931
    goto mandos_end;
633
932
  }
634
933
  
635
934
  if(af == AF_INET6){
636
 
    ret = connect(tcp_sd, &to.in6, sizeof(to));
 
935
    ret = connect(tcp_sd, (struct sockaddr *)&to,
 
936
                  sizeof(struct sockaddr_in6));
637
937
  } else {
638
 
    ret = connect(tcp_sd, &to.in, sizeof(to)); /* IPv4 */
 
938
    ret = connect(tcp_sd, (struct sockaddr *)&to, /* IPv4 */
 
939
                  sizeof(struct sockaddr_in));
639
940
  }
640
941
  if(ret < 0){
641
 
    perror("connect");
642
 
    return -1;
 
942
    if((errno != ECONNREFUSED and errno != ENETUNREACH) or debug){
 
943
      int e = errno;
 
944
      perror_plus("connect");
 
945
      errno = e;
 
946
    }
 
947
    goto mandos_end;
 
948
  }
 
949
  
 
950
  if(quit_now){
 
951
    errno = EINTR;
 
952
    goto mandos_end;
643
953
  }
644
954
  
645
955
  const char *out = mandos_protocol_version;
647
957
  while(true){
648
958
    size_t out_size = strlen(out);
649
959
    ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
650
 
                                   out_size - written));
 
960
                                        out_size - written));
651
961
    if(ret == -1){
652
 
      perror("write");
653
 
      retval = -1;
 
962
      int e = errno;
 
963
      perror_plus("write");
 
964
      errno = e;
654
965
      goto mandos_end;
655
966
    }
656
967
    written += (size_t)ret;
664
975
        break;
665
976
      }
666
977
    }
 
978
  
 
979
    if(quit_now){
 
980
      errno = EINTR;
 
981
      goto mandos_end;
 
982
    }
667
983
  }
668
984
  
669
985
  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{
 
986
    fprintf_plus(stderr, "Establishing TLS session with %s\n", ip);
 
987
  }
 
988
  
 
989
  if(quit_now){
 
990
    errno = EINTR;
 
991
    goto mandos_end;
 
992
  }
 
993
  
 
994
  /* This casting via intptr_t is to eliminate warning about casting
 
995
     an int to a pointer type.  This is exactly how the GnuTLS Guile
 
996
     function "set-session-transport-fd!" does it. */
 
997
  gnutls_transport_set_ptr(session,
 
998
                           (gnutls_transport_ptr_t)(intptr_t)tcp_sd);
 
999
  
 
1000
  if(quit_now){
 
1001
    errno = EINTR;
 
1002
    goto mandos_end;
 
1003
  }
 
1004
  
 
1005
  do {
676
1006
    ret = gnutls_handshake(session);
 
1007
    if(quit_now){
 
1008
      errno = EINTR;
 
1009
      goto mandos_end;
 
1010
    }
677
1011
  } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
678
1012
  
679
1013
  if(ret != GNUTLS_E_SUCCESS){
680
1014
    if(debug){
681
 
      fprintf(stderr, "*** GnuTLS Handshake failed ***\n");
 
1015
      fprintf_plus(stderr, "*** GnuTLS Handshake failed ***\n");
682
1016
      gnutls_perror(ret);
683
1017
    }
684
 
    retval = -1;
 
1018
    errno = EPROTO;
685
1019
    goto mandos_end;
686
1020
  }
687
1021
  
688
1022
  /* Read OpenPGP packet that contains the wanted password */
689
1023
  
690
1024
  if(debug){
691
 
    fprintf(stderr, "Retrieving OpenPGP encrypted password from %s\n",
692
 
            ip);
 
1025
    fprintf_plus(stderr, "Retrieving OpenPGP encrypted password from"
 
1026
                 " %s\n", ip);
693
1027
  }
694
1028
  
695
1029
  while(true){
 
1030
    
 
1031
    if(quit_now){
 
1032
      errno = EINTR;
 
1033
      goto mandos_end;
 
1034
    }
 
1035
    
696
1036
    buffer_capacity = incbuffer(&buffer, buffer_length,
697
 
                                   buffer_capacity);
 
1037
                                buffer_capacity);
698
1038
    if(buffer_capacity == 0){
699
 
      perror("incbuffer");
700
 
      retval = -1;
 
1039
      int e = errno;
 
1040
      perror_plus("incbuffer");
 
1041
      errno = e;
 
1042
      goto mandos_end;
 
1043
    }
 
1044
    
 
1045
    if(quit_now){
 
1046
      errno = EINTR;
701
1047
      goto mandos_end;
702
1048
    }
703
1049
    
712
1058
      case GNUTLS_E_AGAIN:
713
1059
        break;
714
1060
      case GNUTLS_E_REHANDSHAKE:
715
 
        do{
 
1061
        do {
716
1062
          ret = gnutls_handshake(session);
 
1063
          
 
1064
          if(quit_now){
 
1065
            errno = EINTR;
 
1066
            goto mandos_end;
 
1067
          }
717
1068
        } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
718
1069
        if(ret < 0){
719
 
          fprintf(stderr, "*** GnuTLS Re-handshake failed ***\n");
 
1070
          fprintf_plus(stderr, "*** GnuTLS Re-handshake failed "
 
1071
                       "***\n");
720
1072
          gnutls_perror(ret);
721
 
          retval = -1;
 
1073
          errno = EPROTO;
722
1074
          goto mandos_end;
723
1075
        }
724
1076
        break;
725
1077
      default:
726
 
        fprintf(stderr, "Unknown error while reading data from"
727
 
                " encrypted session with Mandos server\n");
728
 
        retval = -1;
 
1078
        fprintf_plus(stderr, "Unknown error while reading data from"
 
1079
                     " encrypted session with Mandos server\n");
729
1080
        gnutls_bye(session, GNUTLS_SHUT_RDWR);
 
1081
        errno = EIO;
730
1082
        goto mandos_end;
731
1083
      }
732
1084
    } else {
735
1087
  }
736
1088
  
737
1089
  if(debug){
738
 
    fprintf(stderr, "Closing TLS session\n");
739
 
  }
740
 
  
741
 
  gnutls_bye(session, GNUTLS_SHUT_RDWR);
 
1090
    fprintf_plus(stderr, "Closing TLS session\n");
 
1091
  }
 
1092
  
 
1093
  if(quit_now){
 
1094
    errno = EINTR;
 
1095
    goto mandos_end;
 
1096
  }
 
1097
  
 
1098
  do {
 
1099
    ret = gnutls_bye(session, GNUTLS_SHUT_RDWR);
 
1100
    if(quit_now){
 
1101
      errno = EINTR;
 
1102
      goto mandos_end;
 
1103
    }
 
1104
  } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
742
1105
  
743
1106
  if(buffer_length > 0){
744
 
    decrypted_buffer_size = pgp_packet_decrypt(buffer,
745
 
                                               buffer_length,
746
 
                                               &decrypted_buffer);
 
1107
    ssize_t decrypted_buffer_size;
 
1108
    decrypted_buffer_size = pgp_packet_decrypt(buffer, buffer_length,
 
1109
                                               &decrypted_buffer, mc);
747
1110
    if(decrypted_buffer_size >= 0){
 
1111
      
748
1112
      written = 0;
749
1113
      while(written < (size_t) decrypted_buffer_size){
 
1114
        if(quit_now){
 
1115
          errno = EINTR;
 
1116
          goto mandos_end;
 
1117
        }
 
1118
        
750
1119
        ret = (int)fwrite(decrypted_buffer + written, 1,
751
1120
                          (size_t)decrypted_buffer_size - written,
752
1121
                          stdout);
753
1122
        if(ret == 0 and ferror(stdout)){
 
1123
          int e = errno;
754
1124
          if(debug){
755
 
            fprintf(stderr, "Error writing encrypted data: %s\n",
756
 
                    strerror(errno));
 
1125
            fprintf_plus(stderr, "Error writing encrypted data: %s\n",
 
1126
                         strerror(errno));
757
1127
          }
758
 
          retval = -1;
759
 
          break;
 
1128
          errno = e;
 
1129
          goto mandos_end;
760
1130
        }
761
1131
        written += (size_t)ret;
762
1132
      }
763
 
      free(decrypted_buffer);
764
 
    } else {
765
 
      retval = -1;
 
1133
      retval = 0;
766
1134
    }
767
 
  } else {
768
 
    retval = -1;
769
1135
  }
770
1136
  
771
1137
  /* Shutdown procedure */
772
1138
  
773
1139
 mandos_end:
774
 
  free(buffer);
775
 
  ret = (int)TEMP_FAILURE_RETRY(close(tcp_sd));
776
 
  if(ret == -1){
777
 
    perror("close");
 
1140
  {
 
1141
    int e = errno;
 
1142
    free(decrypted_buffer);
 
1143
    free(buffer);
 
1144
    if(tcp_sd >= 0){
 
1145
      ret = (int)TEMP_FAILURE_RETRY(close(tcp_sd));
 
1146
    }
 
1147
    if(ret == -1){
 
1148
      if(e == 0){
 
1149
        e = errno;
 
1150
      }
 
1151
      perror_plus("close");
 
1152
    }
 
1153
    gnutls_deinit(session);
 
1154
    errno = e;
 
1155
    if(quit_now){
 
1156
      errno = EINTR;
 
1157
      retval = -1;
 
1158
    }
778
1159
  }
779
 
  gnutls_deinit(session);
780
1160
  return retval;
781
1161
}
782
1162
 
 
1163
__attribute__((nonnull))
783
1164
static void resolve_callback(AvahiSServiceResolver *r,
784
1165
                             AvahiIfIndex interface,
785
1166
                             AvahiProtocol proto,
793
1174
                             AVAHI_GCC_UNUSED AvahiStringList *txt,
794
1175
                             AVAHI_GCC_UNUSED AvahiLookupResultFlags
795
1176
                             flags,
796
 
                             AVAHI_GCC_UNUSED void* userdata){
797
 
  assert(r);
 
1177
                             void *mc){
 
1178
  if(r == NULL){
 
1179
    return;
 
1180
  }
798
1181
  
799
1182
  /* Called whenever a service has been resolved successfully or
800
1183
     timed out */
801
1184
  
 
1185
  if(quit_now){
 
1186
    avahi_s_service_resolver_free(r);
 
1187
    return;
 
1188
  }
 
1189
  
802
1190
  switch(event){
803
1191
  default:
804
1192
  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)));
 
1193
    fprintf_plus(stderr, "(Avahi Resolver) Failed to resolve service "
 
1194
                 "'%s' of type '%s' in domain '%s': %s\n", name, type,
 
1195
                 domain,
 
1196
                 avahi_strerror(avahi_server_errno
 
1197
                                (((mandos_context*)mc)->server)));
808
1198
    break;
809
1199
    
810
1200
  case AVAHI_RESOLVER_FOUND:
812
1202
      char ip[AVAHI_ADDRESS_STR_MAX];
813
1203
      avahi_address_snprint(ip, sizeof(ip), address);
814
1204
      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);
 
1205
        fprintf_plus(stderr, "Mandos server \"%s\" found on %s (%s, %"
 
1206
                     PRIdMAX ") on port %" PRIu16 "\n", name,
 
1207
                     host_name, ip, (intmax_t)interface, port);
818
1208
      }
819
 
      int ret = start_mandos_communication(ip, port, interface,
820
 
                                           avahi_proto_to_af(proto));
 
1209
      int ret = start_mandos_communication(ip, (in_port_t)port,
 
1210
                                           interface,
 
1211
                                           avahi_proto_to_af(proto),
 
1212
                                           mc);
821
1213
      if(ret == 0){
822
 
        avahi_simple_poll_quit(mc.simple_poll);
 
1214
        avahi_simple_poll_quit(simple_poll);
 
1215
      } else {
 
1216
        if(not add_server(ip, (in_port_t)port, interface,
 
1217
                          avahi_proto_to_af(proto),
 
1218
                          &((mandos_context*)mc)->current_server)){
 
1219
          fprintf_plus(stderr, "Failed to add server \"%s\" to server"
 
1220
                       " list\n", name);
 
1221
        }
823
1222
      }
824
1223
    }
825
1224
  }
835
1234
                            const char *domain,
836
1235
                            AVAHI_GCC_UNUSED AvahiLookupResultFlags
837
1236
                            flags,
838
 
                            AVAHI_GCC_UNUSED void* userdata){
839
 
  assert(b);
 
1237
                            void *mc){
 
1238
  if(b == NULL){
 
1239
    return;
 
1240
  }
840
1241
  
841
1242
  /* Called whenever a new services becomes available on the LAN or
842
1243
     is removed from the LAN */
843
1244
  
 
1245
  if(quit_now){
 
1246
    return;
 
1247
  }
 
1248
  
844
1249
  switch(event){
845
1250
  default:
846
1251
  case AVAHI_BROWSER_FAILURE:
847
1252
    
848
 
    fprintf(stderr, "(Avahi browser) %s\n",
849
 
            avahi_strerror(avahi_server_errno(mc.server)));
850
 
    avahi_simple_poll_quit(mc.simple_poll);
 
1253
    fprintf_plus(stderr, "(Avahi browser) %s\n",
 
1254
                 avahi_strerror(avahi_server_errno
 
1255
                                (((mandos_context*)mc)->server)));
 
1256
    avahi_simple_poll_quit(simple_poll);
851
1257
    return;
852
1258
    
853
1259
  case AVAHI_BROWSER_NEW:
856
1262
       the callback function is called the Avahi server will free the
857
1263
       resolver for us. */
858
1264
    
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)));
 
1265
    if(avahi_s_service_resolver_new(((mandos_context*)mc)->server,
 
1266
                                    interface, protocol, name, type,
 
1267
                                    domain, protocol, 0,
 
1268
                                    resolve_callback, mc) == NULL)
 
1269
      fprintf_plus(stderr, "Avahi: Failed to resolve service '%s':"
 
1270
                   " %s\n", name,
 
1271
                   avahi_strerror(avahi_server_errno
 
1272
                                  (((mandos_context*)mc)->server)));
864
1273
    break;
865
1274
    
866
1275
  case AVAHI_BROWSER_REMOVE:
869
1278
  case AVAHI_BROWSER_ALL_FOR_NOW:
870
1279
  case AVAHI_BROWSER_CACHE_EXHAUSTED:
871
1280
    if(debug){
872
 
      fprintf(stderr, "No Mandos server found, still searching...\n");
 
1281
      fprintf_plus(stderr, "No Mandos server found, still"
 
1282
                   " searching...\n");
873
1283
    }
874
1284
    break;
875
1285
  }
876
1286
}
877
1287
 
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){
 
1288
/* Signal handler that stops main loop after SIGTERM */
 
1289
static void handle_sigterm(int sig){
882
1290
  if(quit_now){
883
1291
    return;
884
1292
  }
885
1293
  quit_now = 1;
 
1294
  signal_received = sig;
886
1295
  int old_errno = errno;
887
 
  if(mc.simple_poll != NULL){
888
 
    avahi_simple_poll_quit(mc.simple_poll);
889
 
  }
890
 
  errno = old_errno;
 
1296
  /* set main loop to exit */
 
1297
  if(simple_poll != NULL){
 
1298
    avahi_simple_poll_quit(simple_poll);
 
1299
  }
 
1300
  errno = old_errno;
 
1301
}
 
1302
 
 
1303
__attribute__((nonnull, warn_unused_result))
 
1304
bool get_flags(const char *ifname, struct ifreq *ifr){
 
1305
  int ret;
 
1306
  error_t ret_errno;
 
1307
  
 
1308
  int s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
 
1309
  if(s < 0){
 
1310
    ret_errno = errno;
 
1311
    perror_plus("socket");
 
1312
    errno = ret_errno;
 
1313
    return false;
 
1314
  }
 
1315
  strcpy(ifr->ifr_name, ifname);
 
1316
  ret = ioctl(s, SIOCGIFFLAGS, ifr);
 
1317
  if(ret == -1){
 
1318
    if(debug){
 
1319
      ret_errno = errno;
 
1320
      perror_plus("ioctl SIOCGIFFLAGS");
 
1321
      errno = ret_errno;
 
1322
    }
 
1323
    return false;
 
1324
  }
 
1325
  return true;
 
1326
}
 
1327
 
 
1328
__attribute__((nonnull, warn_unused_result))
 
1329
bool good_flags(const char *ifname, const struct ifreq *ifr){
 
1330
  
 
1331
  /* Reject the loopback device */
 
1332
  if(ifr->ifr_flags & IFF_LOOPBACK){
 
1333
    if(debug){
 
1334
      fprintf_plus(stderr, "Rejecting loopback interface \"%s\"\n",
 
1335
                   ifname);
 
1336
    }
 
1337
    return false;
 
1338
  }
 
1339
  /* Accept point-to-point devices only if connect_to is specified */
 
1340
  if(connect_to != NULL and (ifr->ifr_flags & IFF_POINTOPOINT)){
 
1341
    if(debug){
 
1342
      fprintf_plus(stderr, "Accepting point-to-point interface"
 
1343
                   " \"%s\"\n", ifname);
 
1344
    }
 
1345
    return true;
 
1346
  }
 
1347
  /* Otherwise, reject non-broadcast-capable devices */
 
1348
  if(not (ifr->ifr_flags & IFF_BROADCAST)){
 
1349
    if(debug){
 
1350
      fprintf_plus(stderr, "Rejecting non-broadcast interface"
 
1351
                   " \"%s\"\n", ifname);
 
1352
    }
 
1353
    return false;
 
1354
  }
 
1355
  /* Reject non-ARP interfaces (including dummy interfaces) */
 
1356
  if(ifr->ifr_flags & IFF_NOARP){
 
1357
    if(debug){
 
1358
      fprintf_plus(stderr, "Rejecting non-ARP interface \"%s\"\n",
 
1359
                   ifname);
 
1360
    }
 
1361
    return false;
 
1362
  }
 
1363
  
 
1364
  /* Accept this device */
 
1365
  if(debug){
 
1366
    fprintf_plus(stderr, "Interface \"%s\" is good\n", ifname);
 
1367
  }
 
1368
  return true;
 
1369
}
 
1370
 
 
1371
/* 
 
1372
 * This function determines if a directory entry in /sys/class/net
 
1373
 * corresponds to an acceptable network device.
 
1374
 * (This function is passed to scandir(3) as a filter function.)
 
1375
 */
 
1376
__attribute__((nonnull, warn_unused_result))
 
1377
int good_interface(const struct dirent *if_entry){
 
1378
  if(if_entry->d_name[0] == '.'){
 
1379
    return 0;
 
1380
  }
 
1381
  
 
1382
  struct ifreq ifr;
 
1383
  if(not get_flags(if_entry->d_name, &ifr)){
 
1384
    if(debug){
 
1385
      fprintf_plus(stderr, "Failed to get flags for interface "
 
1386
                   "\"%s\"\n", if_entry->d_name);
 
1387
    }
 
1388
    return 0;
 
1389
  }
 
1390
  
 
1391
  if(not good_flags(if_entry->d_name, &ifr)){
 
1392
    return 0;
 
1393
  }
 
1394
  return 1;
 
1395
}
 
1396
 
 
1397
/* 
 
1398
 * This function determines if a network interface is up.
 
1399
 */
 
1400
__attribute__((nonnull, warn_unused_result))
 
1401
bool interface_is_up(const char *interface){
 
1402
  struct ifreq ifr;
 
1403
  if(not get_flags(interface, &ifr)){
 
1404
    if(debug){
 
1405
      fprintf_plus(stderr, "Failed to get flags for interface "
 
1406
                   "\"%s\"\n", interface);
 
1407
    }
 
1408
    return false;
 
1409
  }
 
1410
  
 
1411
  return (bool)(ifr.ifr_flags & IFF_UP);
 
1412
}
 
1413
 
 
1414
/* 
 
1415
 * This function determines if a network interface is running
 
1416
 */
 
1417
__attribute__((nonnull, warn_unused_result))
 
1418
bool interface_is_running(const char *interface){
 
1419
  struct ifreq ifr;
 
1420
  if(not get_flags(interface, &ifr)){
 
1421
    if(debug){
 
1422
      fprintf_plus(stderr, "Failed to get flags for interface "
 
1423
                   "\"%s\"\n", interface);
 
1424
    }
 
1425
    return false;
 
1426
  }
 
1427
  
 
1428
  return (bool)(ifr.ifr_flags & IFF_RUNNING);
 
1429
}
 
1430
 
 
1431
__attribute__((nonnull, pure, warn_unused_result))
 
1432
int notdotentries(const struct dirent *direntry){
 
1433
  /* Skip "." and ".." */
 
1434
  if(direntry->d_name[0] == '.'
 
1435
     and (direntry->d_name[1] == '\0'
 
1436
          or (direntry->d_name[1] == '.'
 
1437
              and direntry->d_name[2] == '\0'))){
 
1438
    return 0;
 
1439
  }
 
1440
  return 1;
 
1441
}
 
1442
 
 
1443
/* Is this directory entry a runnable program? */
 
1444
__attribute__((nonnull, warn_unused_result))
 
1445
int runnable_hook(const struct dirent *direntry){
 
1446
  int ret;
 
1447
  size_t sret;
 
1448
  struct stat st;
 
1449
  
 
1450
  if((direntry->d_name)[0] == '\0'){
 
1451
    /* Empty name? */
 
1452
    return 0;
 
1453
  }
 
1454
  
 
1455
  sret = strspn(direntry->d_name, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
 
1456
                "abcdefghijklmnopqrstuvwxyz"
 
1457
                "0123456789"
 
1458
                "_.-");
 
1459
  if((direntry->d_name)[sret] != '\0'){
 
1460
    /* Contains non-allowed characters */
 
1461
    if(debug){
 
1462
      fprintf_plus(stderr, "Ignoring hook \"%s\" with bad name\n",
 
1463
                   direntry->d_name);
 
1464
    }
 
1465
    return 0;
 
1466
  }
 
1467
  
 
1468
  ret = fstatat(hookdir_fd, direntry->d_name, &st, 0);
 
1469
  if(ret == -1){
 
1470
    if(debug){
 
1471
      perror_plus("Could not stat hook");
 
1472
    }
 
1473
    return 0;
 
1474
  }
 
1475
  if(not (S_ISREG(st.st_mode))){
 
1476
    /* Not a regular file */
 
1477
    if(debug){
 
1478
      fprintf_plus(stderr, "Ignoring hook \"%s\" - not a file\n",
 
1479
                   direntry->d_name);
 
1480
    }
 
1481
    return 0;
 
1482
  }
 
1483
  if(not (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))){
 
1484
    /* Not executable */
 
1485
    if(debug){
 
1486
      fprintf_plus(stderr, "Ignoring hook \"%s\" - not executable\n",
 
1487
                   direntry->d_name);
 
1488
    }
 
1489
    return 0;
 
1490
  }
 
1491
  if(debug){
 
1492
    fprintf_plus(stderr, "Hook \"%s\" is acceptable\n",
 
1493
                 direntry->d_name);
 
1494
  }
 
1495
  return 1;
 
1496
}
 
1497
 
 
1498
__attribute__((nonnull, warn_unused_result))
 
1499
int avahi_loop_with_timeout(AvahiSimplePoll *s, int retry_interval,
 
1500
                            mandos_context *mc){
 
1501
  int ret;
 
1502
  struct timespec now;
 
1503
  struct timespec waited_time;
 
1504
  intmax_t block_time;
 
1505
  
 
1506
  while(true){
 
1507
    if(mc->current_server == NULL){
 
1508
      if(debug){
 
1509
        fprintf_plus(stderr, "Wait until first server is found."
 
1510
                     " No timeout!\n");
 
1511
      }
 
1512
      ret = avahi_simple_poll_iterate(s, -1);
 
1513
    } else {
 
1514
      if(debug){
 
1515
        fprintf_plus(stderr, "Check current_server if we should run"
 
1516
                     " it, or wait\n");
 
1517
      }
 
1518
      /* the current time */
 
1519
      ret = clock_gettime(CLOCK_MONOTONIC, &now);
 
1520
      if(ret == -1){
 
1521
        perror_plus("clock_gettime");
 
1522
        return -1;
 
1523
      }
 
1524
      /* Calculating in ms how long time between now and server
 
1525
         who we visted longest time ago. Now - last seen.  */
 
1526
      waited_time.tv_sec = (now.tv_sec
 
1527
                            - mc->current_server->last_seen.tv_sec);
 
1528
      waited_time.tv_nsec = (now.tv_nsec
 
1529
                             - mc->current_server->last_seen.tv_nsec);
 
1530
      /* total time is 10s/10,000ms.
 
1531
         Converting to s from ms by dividing by 1,000,
 
1532
         and ns to ms by dividing by 1,000,000. */
 
1533
      block_time = ((retry_interval
 
1534
                     - ((intmax_t)waited_time.tv_sec * 1000))
 
1535
                    - ((intmax_t)waited_time.tv_nsec / 1000000));
 
1536
      
 
1537
      if(debug){
 
1538
        fprintf_plus(stderr, "Blocking for %" PRIdMAX " ms\n",
 
1539
                     block_time);
 
1540
      }
 
1541
      
 
1542
      if(block_time <= 0){
 
1543
        ret = start_mandos_communication(mc->current_server->ip,
 
1544
                                         mc->current_server->port,
 
1545
                                         mc->current_server->if_index,
 
1546
                                         mc->current_server->af, mc);
 
1547
        if(ret == 0){
 
1548
          avahi_simple_poll_quit(s);
 
1549
          return 0;
 
1550
        }
 
1551
        ret = clock_gettime(CLOCK_MONOTONIC,
 
1552
                            &mc->current_server->last_seen);
 
1553
        if(ret == -1){
 
1554
          perror_plus("clock_gettime");
 
1555
          return -1;
 
1556
        }
 
1557
        mc->current_server = mc->current_server->next;
 
1558
        block_time = 0;         /* Call avahi to find new Mandos
 
1559
                                   servers, but don't block */
 
1560
      }
 
1561
      
 
1562
      ret = avahi_simple_poll_iterate(s, (int)block_time);
 
1563
    }
 
1564
    if(ret != 0){
 
1565
      if(ret > 0 or errno != EINTR){
 
1566
        return (ret != 1) ? ret : 0;
 
1567
      }
 
1568
    }
 
1569
  }
 
1570
}
 
1571
 
 
1572
/* Set effective uid to 0, return errno */
 
1573
__attribute__((warn_unused_result))
 
1574
error_t raise_privileges(void){
 
1575
  error_t old_errno = errno;
 
1576
  error_t ret_errno = 0;
 
1577
  if(seteuid(0) == -1){
 
1578
    ret_errno = errno;
 
1579
  }
 
1580
  errno = old_errno;
 
1581
  return ret_errno;
 
1582
}
 
1583
 
 
1584
/* Set effective and real user ID to 0.  Return errno. */
 
1585
__attribute__((warn_unused_result))
 
1586
error_t raise_privileges_permanently(void){
 
1587
  error_t old_errno = errno;
 
1588
  error_t ret_errno = raise_privileges();
 
1589
  if(ret_errno != 0){
 
1590
    errno = old_errno;
 
1591
    return ret_errno;
 
1592
  }
 
1593
  if(setuid(0) == -1){
 
1594
    ret_errno = errno;
 
1595
  }
 
1596
  errno = old_errno;
 
1597
  return ret_errno;
 
1598
}
 
1599
 
 
1600
/* Set effective user ID to unprivileged saved user ID */
 
1601
__attribute__((warn_unused_result))
 
1602
error_t lower_privileges(void){
 
1603
  error_t old_errno = errno;
 
1604
  error_t ret_errno = 0;
 
1605
  if(seteuid(uid) == -1){
 
1606
    ret_errno = errno;
 
1607
  }
 
1608
  errno = old_errno;
 
1609
  return ret_errno;
 
1610
}
 
1611
 
 
1612
/* Lower privileges permanently */
 
1613
__attribute__((warn_unused_result))
 
1614
error_t lower_privileges_permanently(void){
 
1615
  error_t old_errno = errno;
 
1616
  error_t ret_errno = 0;
 
1617
  if(setuid(uid) == -1){
 
1618
    ret_errno = errno;
 
1619
  }
 
1620
  errno = old_errno;
 
1621
  return ret_errno;
 
1622
}
 
1623
 
 
1624
__attribute__((nonnull))
 
1625
void run_network_hooks(const char *mode, const char *interface,
 
1626
                       const float delay){
 
1627
  struct dirent **direntries = NULL;
 
1628
  if(hookdir_fd == -1){
 
1629
    hookdir_fd = open(hookdir, O_RDONLY);
 
1630
    if(hookdir_fd == -1){
 
1631
      if(errno == ENOENT){
 
1632
        if(debug){
 
1633
          fprintf_plus(stderr, "Network hook directory \"%s\" not"
 
1634
                       " found\n", hookdir);
 
1635
        }
 
1636
      } else {
 
1637
        perror_plus("open");
 
1638
      }
 
1639
      return;
 
1640
    }
 
1641
  }
 
1642
#ifdef __GLIBC__
 
1643
#if __GLIBC_PREREQ(2, 15)
 
1644
  int numhooks = scandirat(hookdir_fd, ".", &direntries,
 
1645
                           runnable_hook, alphasort);
 
1646
#else  /* not __GLIBC_PREREQ(2, 15) */
 
1647
  int numhooks = scandir(hookdir, &direntries, runnable_hook,
 
1648
                         alphasort);
 
1649
#endif  /* not __GLIBC_PREREQ(2, 15) */
 
1650
#else   /* not __GLIBC__ */
 
1651
  int numhooks = scandir(hookdir, &direntries, runnable_hook,
 
1652
                         alphasort);
 
1653
#endif  /* not __GLIBC__ */
 
1654
  if(numhooks == -1){
 
1655
    perror_plus("scandir");
 
1656
    return;
 
1657
  }
 
1658
  struct dirent *direntry;
 
1659
  int ret;
 
1660
  int devnull = open("/dev/null", O_RDONLY);
 
1661
  for(int i = 0; i < numhooks; i++){
 
1662
    direntry = direntries[i];
 
1663
    if(debug){
 
1664
      fprintf_plus(stderr, "Running network hook \"%s\"\n",
 
1665
                   direntry->d_name);
 
1666
    }
 
1667
    pid_t hook_pid = fork();
 
1668
    if(hook_pid == 0){
 
1669
      /* Child */
 
1670
      /* Raise privileges */
 
1671
      errno = raise_privileges_permanently();
 
1672
      if(errno != 0){
 
1673
        perror_plus("Failed to raise privileges");
 
1674
        _exit(EX_NOPERM);
 
1675
      }
 
1676
      /* Set group */
 
1677
      errno = 0;
 
1678
      ret = setgid(0);
 
1679
      if(ret == -1){
 
1680
        perror_plus("setgid");
 
1681
        _exit(EX_NOPERM);
 
1682
      }
 
1683
      /* Reset supplementary groups */
 
1684
      errno = 0;
 
1685
      ret = setgroups(0, NULL);
 
1686
      if(ret == -1){
 
1687
        perror_plus("setgroups");
 
1688
        _exit(EX_NOPERM);
 
1689
      }
 
1690
      ret = dup2(devnull, STDIN_FILENO);
 
1691
      if(ret == -1){
 
1692
        perror_plus("dup2(devnull, STDIN_FILENO)");
 
1693
        _exit(EX_OSERR);
 
1694
      }
 
1695
      ret = close(devnull);
 
1696
      if(ret == -1){
 
1697
        perror_plus("close");
 
1698
        _exit(EX_OSERR);
 
1699
      }
 
1700
      ret = dup2(STDERR_FILENO, STDOUT_FILENO);
 
1701
      if(ret == -1){
 
1702
        perror_plus("dup2(STDERR_FILENO, STDOUT_FILENO)");
 
1703
        _exit(EX_OSERR);
 
1704
      }
 
1705
      ret = setenv("MANDOSNETHOOKDIR", hookdir, 1);
 
1706
      if(ret == -1){
 
1707
        perror_plus("setenv");
 
1708
        _exit(EX_OSERR);
 
1709
      }
 
1710
      ret = setenv("DEVICE", interface, 1);
 
1711
      if(ret == -1){
 
1712
        perror_plus("setenv");
 
1713
        _exit(EX_OSERR);
 
1714
      }
 
1715
      ret = setenv("VERBOSITY", debug ? "1" : "0", 1);
 
1716
      if(ret == -1){
 
1717
        perror_plus("setenv");
 
1718
        _exit(EX_OSERR);
 
1719
      }
 
1720
      ret = setenv("MODE", mode, 1);
 
1721
      if(ret == -1){
 
1722
        perror_plus("setenv");
 
1723
        _exit(EX_OSERR);
 
1724
      }
 
1725
      char *delaystring;
 
1726
      ret = asprintf(&delaystring, "%f", (double)delay);
 
1727
      if(ret == -1){
 
1728
        perror_plus("asprintf");
 
1729
        _exit(EX_OSERR);
 
1730
      }
 
1731
      ret = setenv("DELAY", delaystring, 1);
 
1732
      if(ret == -1){
 
1733
        free(delaystring);
 
1734
        perror_plus("setenv");
 
1735
        _exit(EX_OSERR);
 
1736
      }
 
1737
      free(delaystring);
 
1738
      if(connect_to != NULL){
 
1739
        ret = setenv("CONNECT", connect_to, 1);
 
1740
        if(ret == -1){
 
1741
          perror_plus("setenv");
 
1742
          _exit(EX_OSERR);
 
1743
        }
 
1744
      }
 
1745
      int hook_fd = openat(hookdir_fd, direntry->d_name, O_RDONLY);
 
1746
      if(hook_fd == -1){
 
1747
        perror_plus("openat");
 
1748
        _exit(EXIT_FAILURE);
 
1749
      }
 
1750
      if((int)TEMP_FAILURE_RETRY(close(hookdir_fd)) == -1){
 
1751
        perror_plus("close");
 
1752
        _exit(EXIT_FAILURE);
 
1753
      }
 
1754
      if(fexecve(hook_fd, (char *const []){ direntry->d_name, NULL },
 
1755
                 environ) == -1){
 
1756
        perror_plus("fexecve");
 
1757
        _exit(EXIT_FAILURE);
 
1758
      }
 
1759
    } else {
 
1760
      if(hook_pid == -1){
 
1761
        perror_plus("fork");
 
1762
        free(direntry);
 
1763
        continue;
 
1764
      }
 
1765
      int status;
 
1766
      if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){
 
1767
        perror_plus("waitpid");
 
1768
        free(direntry);
 
1769
        continue;
 
1770
      }
 
1771
      if(WIFEXITED(status)){
 
1772
        if(WEXITSTATUS(status) != 0){
 
1773
          fprintf_plus(stderr, "Warning: network hook \"%s\" exited"
 
1774
                       " with status %d\n", direntry->d_name,
 
1775
                       WEXITSTATUS(status));
 
1776
          free(direntry);
 
1777
          continue;
 
1778
        }
 
1779
      } else if(WIFSIGNALED(status)){
 
1780
        fprintf_plus(stderr, "Warning: network hook \"%s\" died by"
 
1781
                     " signal %d\n", direntry->d_name,
 
1782
                     WTERMSIG(status));
 
1783
        free(direntry);
 
1784
        continue;
 
1785
      } else {
 
1786
        fprintf_plus(stderr, "Warning: network hook \"%s\""
 
1787
                     " crashed\n", direntry->d_name);
 
1788
        free(direntry);
 
1789
        continue;
 
1790
      }
 
1791
    }
 
1792
    if(debug){
 
1793
      fprintf_plus(stderr, "Network hook \"%s\" ran successfully\n",
 
1794
                   direntry->d_name);
 
1795
    }
 
1796
    free(direntry);
 
1797
  }
 
1798
  free(direntries);
 
1799
  if((int)TEMP_FAILURE_RETRY(close(hookdir_fd)) == -1){
 
1800
    perror_plus("close");
 
1801
  } else {
 
1802
    hookdir_fd = -1;
 
1803
  }
 
1804
  close(devnull);
 
1805
}
 
1806
 
 
1807
__attribute__((nonnull, warn_unused_result))
 
1808
error_t bring_up_interface(const char *const interface,
 
1809
                           const float delay){
 
1810
  error_t old_errno = errno;
 
1811
  int ret;
 
1812
  struct ifreq network;
 
1813
  unsigned int if_index = if_nametoindex(interface);
 
1814
  if(if_index == 0){
 
1815
    fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
 
1816
    errno = old_errno;
 
1817
    return ENXIO;
 
1818
  }
 
1819
  
 
1820
  if(quit_now){
 
1821
    errno = old_errno;
 
1822
    return EINTR;
 
1823
  }
 
1824
  
 
1825
  if(not interface_is_up(interface)){
 
1826
    error_t ret_errno = 0, ioctl_errno = 0;
 
1827
    if(not get_flags(interface, &network)){
 
1828
      ret_errno = errno;
 
1829
      fprintf_plus(stderr, "Failed to get flags for interface "
 
1830
                   "\"%s\"\n", interface);
 
1831
      errno = old_errno;
 
1832
      return ret_errno;
 
1833
    }
 
1834
    network.ifr_flags |= IFF_UP; /* set flag */
 
1835
    
 
1836
    int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
 
1837
    if(sd == -1){
 
1838
      ret_errno = errno;
 
1839
      perror_plus("socket");
 
1840
      errno = old_errno;
 
1841
      return ret_errno;
 
1842
    }
 
1843
    
 
1844
    if(quit_now){
 
1845
      ret = (int)TEMP_FAILURE_RETRY(close(sd));
 
1846
      if(ret == -1){
 
1847
        perror_plus("close");
 
1848
      }
 
1849
      errno = old_errno;
 
1850
      return EINTR;
 
1851
    }
 
1852
    
 
1853
    if(debug){
 
1854
      fprintf_plus(stderr, "Bringing up interface \"%s\"\n",
 
1855
                   interface);
 
1856
    }
 
1857
    
 
1858
    /* Raise privileges */
 
1859
    ret_errno = raise_privileges();
 
1860
    if(ret_errno != 0){
 
1861
      errno = ret_errno;
 
1862
      perror_plus("Failed to raise privileges");
 
1863
    }
 
1864
    
 
1865
#ifdef __linux__
 
1866
    int ret_linux;
 
1867
    bool restore_loglevel = false;
 
1868
    if(ret_errno == 0){
 
1869
      /* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
 
1870
         messages about the network interface to mess up the prompt */
 
1871
      ret_linux = klogctl(8, NULL, 5);
 
1872
      if(ret_linux == -1){
 
1873
        perror_plus("klogctl");
 
1874
      } else {
 
1875
        restore_loglevel = true;
 
1876
      }
 
1877
    }
 
1878
#endif  /* __linux__ */
 
1879
    int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
 
1880
    ioctl_errno = errno;
 
1881
#ifdef __linux__
 
1882
    if(restore_loglevel){
 
1883
      ret_linux = klogctl(7, NULL, 0);
 
1884
      if(ret_linux == -1){
 
1885
        perror_plus("klogctl");
 
1886
      }
 
1887
    }
 
1888
#endif  /* __linux__ */
 
1889
    
 
1890
    /* If raise_privileges() succeeded above */
 
1891
    if(ret_errno == 0){
 
1892
      /* Lower privileges */
 
1893
      ret_errno = lower_privileges();
 
1894
      if(ret_errno != 0){
 
1895
        errno = ret_errno;
 
1896
        perror_plus("Failed to lower privileges");
 
1897
      }
 
1898
    }
 
1899
    
 
1900
    /* Close the socket */
 
1901
    ret = (int)TEMP_FAILURE_RETRY(close(sd));
 
1902
    if(ret == -1){
 
1903
      perror_plus("close");
 
1904
    }
 
1905
    
 
1906
    if(ret_setflags == -1){
 
1907
      errno = ioctl_errno;
 
1908
      perror_plus("ioctl SIOCSIFFLAGS +IFF_UP");
 
1909
      errno = old_errno;
 
1910
      return ioctl_errno;
 
1911
    }
 
1912
  } else if(debug){
 
1913
    fprintf_plus(stderr, "Interface \"%s\" is already up; good\n",
 
1914
                 interface);
 
1915
  }
 
1916
  
 
1917
  /* Sleep checking until interface is running.
 
1918
     Check every 0.25s, up to total time of delay */
 
1919
  for(int i=0; i < delay * 4; i++){
 
1920
    if(interface_is_running(interface)){
 
1921
      break;
 
1922
    }
 
1923
    struct timespec sleeptime = { .tv_nsec = 250000000 };
 
1924
    ret = nanosleep(&sleeptime, NULL);
 
1925
    if(ret == -1 and errno != EINTR){
 
1926
      perror_plus("nanosleep");
 
1927
    }
 
1928
  }
 
1929
  
 
1930
  errno = old_errno;
 
1931
  return 0;
 
1932
}
 
1933
 
 
1934
__attribute__((nonnull, warn_unused_result))
 
1935
error_t take_down_interface(const char *const interface){
 
1936
  error_t old_errno = errno;
 
1937
  struct ifreq network;
 
1938
  unsigned int if_index = if_nametoindex(interface);
 
1939
  if(if_index == 0){
 
1940
    fprintf_plus(stderr, "No such interface: \"%s\"\n", interface);
 
1941
    errno = old_errno;
 
1942
    return ENXIO;
 
1943
  }
 
1944
  if(interface_is_up(interface)){
 
1945
    error_t ret_errno = 0, ioctl_errno = 0;
 
1946
    if(not get_flags(interface, &network) and debug){
 
1947
      ret_errno = errno;
 
1948
      fprintf_plus(stderr, "Failed to get flags for interface "
 
1949
                   "\"%s\"\n", interface);
 
1950
      errno = old_errno;
 
1951
      return ret_errno;
 
1952
    }
 
1953
    network.ifr_flags &= ~(short)IFF_UP; /* clear flag */
 
1954
    
 
1955
    int sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
 
1956
    if(sd == -1){
 
1957
      ret_errno = errno;
 
1958
      perror_plus("socket");
 
1959
      errno = old_errno;
 
1960
      return ret_errno;
 
1961
    }
 
1962
    
 
1963
    if(debug){
 
1964
      fprintf_plus(stderr, "Taking down interface \"%s\"\n",
 
1965
                   interface);
 
1966
    }
 
1967
    
 
1968
    /* Raise privileges */
 
1969
    ret_errno = raise_privileges();
 
1970
    if(ret_errno != 0){
 
1971
      errno = ret_errno;
 
1972
      perror_plus("Failed to raise privileges");
 
1973
    }
 
1974
    
 
1975
    int ret_setflags = ioctl(sd, SIOCSIFFLAGS, &network);
 
1976
    ioctl_errno = errno;
 
1977
    
 
1978
    /* If raise_privileges() succeeded above */
 
1979
    if(ret_errno == 0){
 
1980
      /* Lower privileges */
 
1981
      ret_errno = lower_privileges();
 
1982
      if(ret_errno != 0){
 
1983
        errno = ret_errno;
 
1984
        perror_plus("Failed to lower privileges");
 
1985
      }
 
1986
    }
 
1987
    
 
1988
    /* Close the socket */
 
1989
    int ret = (int)TEMP_FAILURE_RETRY(close(sd));
 
1990
    if(ret == -1){
 
1991
      perror_plus("close");
 
1992
    }
 
1993
    
 
1994
    if(ret_setflags == -1){
 
1995
      errno = ioctl_errno;
 
1996
      perror_plus("ioctl SIOCSIFFLAGS -IFF_UP");
 
1997
      errno = old_errno;
 
1998
      return ioctl_errno;
 
1999
    }
 
2000
  } else if(debug){
 
2001
    fprintf_plus(stderr, "Interface \"%s\" is already down; odd\n",
 
2002
                 interface);
 
2003
  }
 
2004
  
 
2005
  errno = old_errno;
 
2006
  return 0;
891
2007
}
892
2008
 
893
2009
int main(int argc, char *argv[]){
 
2010
  mandos_context mc = { .server = NULL, .dh_bits = 0,
 
2011
                        .priority = "SECURE256:!CTYPE-X.509:"
 
2012
                        "+CTYPE-OPENPGP:!RSA", .current_server = NULL,
 
2013
                        .interfaces = NULL, .interfaces_size = 0 };
894
2014
  AvahiSServiceBrowser *sb = NULL;
895
 
  int error;
 
2015
  error_t ret_errno;
896
2016
  int ret;
897
2017
  intmax_t tmpmax;
898
2018
  char *tmp;
899
2019
  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;
 
2020
  char *interfaces_to_take_down = NULL;
 
2021
  size_t interfaces_to_take_down_size = 0;
 
2022
  char run_tempdir[] = "/run/tmp/mandosXXXXXX";
 
2023
  char old_tempdir[] = "/tmp/mandosXXXXXX";
 
2024
  char *tempdir = NULL;
909
2025
  AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
910
2026
  const char *seckey = PATHDIR "/" SECKEY;
911
2027
  const char *pubkey = PATHDIR "/" PUBKEY;
 
2028
  char *interfaces_hooks = NULL;
912
2029
  
913
2030
  bool gnutls_initialized = false;
914
2031
  bool gpgme_initialized = false;
915
2032
  float delay = 2.5f;
 
2033
  double retry_interval = 10; /* 10s between trying a server and
 
2034
                                 retrying the same server again */
916
2035
  
917
 
  struct sigaction old_sigterm_action;
 
2036
  struct sigaction old_sigterm_action = { .sa_handler = SIG_DFL };
918
2037
  struct sigaction sigterm_action = { .sa_handler = handle_sigterm };
919
2038
  
 
2039
  uid = getuid();
 
2040
  gid = getgid();
 
2041
  
 
2042
  /* Lower any group privileges we might have, just to be safe */
 
2043
  errno = 0;
 
2044
  ret = setgid(gid);
 
2045
  if(ret == -1){
 
2046
    perror_plus("setgid");
 
2047
  }
 
2048
  
 
2049
  /* Lower user privileges (temporarily) */
 
2050
  errno = 0;
 
2051
  ret = seteuid(uid);
 
2052
  if(ret == -1){
 
2053
    perror_plus("seteuid");
 
2054
  }
 
2055
  
 
2056
  if(quit_now){
 
2057
    goto end;
 
2058
  }
 
2059
  
920
2060
  {
921
2061
    struct argp_option options[] = {
922
2062
      { .name = "debug", .key = 128,
951
2091
        .arg = "SECONDS",
952
2092
        .doc = "Maximum delay to wait for interface startup",
953
2093
        .group = 2 },
 
2094
      { .name = "retry", .key = 132,
 
2095
        .arg = "SECONDS",
 
2096
        .doc = "Retry interval used when denied by the Mandos server",
 
2097
        .group = 2 },
 
2098
      { .name = "network-hook-dir", .key = 133,
 
2099
        .arg = "DIR",
 
2100
        .doc = "Directory where network hooks are located",
 
2101
        .group = 2 },
 
2102
      /*
 
2103
       * These reproduce what we would get without ARGP_NO_HELP
 
2104
       */
 
2105
      { .name = "help", .key = '?',
 
2106
        .doc = "Give this help list", .group = -1 },
 
2107
      { .name = "usage", .key = -3,
 
2108
        .doc = "Give a short usage message", .group = -1 },
 
2109
      { .name = "version", .key = 'V',
 
2110
        .doc = "Print program version", .group = -1 },
954
2111
      { .name = NULL }
955
2112
    };
956
2113
    
957
2114
    error_t parse_opt(int key, char *arg,
958
2115
                      struct argp_state *state){
 
2116
      errno = 0;
959
2117
      switch(key){
960
2118
      case 128:                 /* --debug */
961
2119
        debug = true;
964
2122
        connect_to = arg;
965
2123
        break;
966
2124
      case 'i':                 /* --interface */
967
 
        interface = arg;
 
2125
        ret_errno = argz_add_sep(&mc.interfaces, &mc.interfaces_size,
 
2126
                                 arg, (int)',');
 
2127
        if(ret_errno != 0){
 
2128
          argp_error(state, "%s", strerror(ret_errno));
 
2129
        }
968
2130
        break;
969
2131
      case 's':                 /* --seckey */
970
2132
        seckey = arg;
977
2139
        tmpmax = strtoimax(arg, &tmp, 10);
978
2140
        if(errno != 0 or tmp == arg or *tmp != '\0'
979
2141
           or tmpmax != (typeof(mc.dh_bits))tmpmax){
980
 
          fprintf(stderr, "Bad number of DH bits\n");
981
 
          exit(EXIT_FAILURE);
 
2142
          argp_error(state, "Bad number of DH bits");
982
2143
        }
983
2144
        mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
984
2145
        break;
989
2150
        errno = 0;
990
2151
        delay = strtof(arg, &tmp);
991
2152
        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:
 
2153
          argp_error(state, "Bad delay");
 
2154
        }
 
2155
      case 132:                 /* --retry */
 
2156
        errno = 0;
 
2157
        retry_interval = strtod(arg, &tmp);
 
2158
        if(errno != 0 or tmp == arg or *tmp != '\0'
 
2159
           or (retry_interval * 1000) > INT_MAX
 
2160
           or retry_interval < 0){
 
2161
          argp_error(state, "Bad retry interval");
 
2162
        }
 
2163
        break;
 
2164
      case 133:                 /* --network-hook-dir */
 
2165
        hookdir = arg;
 
2166
        break;
 
2167
        /*
 
2168
         * These reproduce what we would get without ARGP_NO_HELP
 
2169
         */
 
2170
      case '?':                 /* --help */
 
2171
        argp_state_help(state, state->out_stream,
 
2172
                        (ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR)
 
2173
                        & ~(unsigned int)ARGP_HELP_EXIT_OK);
 
2174
      case -3:                  /* --usage */
 
2175
        argp_state_help(state, state->out_stream,
 
2176
                        ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
 
2177
      case 'V':                 /* --version */
 
2178
        fprintf_plus(state->out_stream, "%s\n", argp_program_version);
 
2179
        exit(argp_err_exit_status);
999
2180
        break;
1000
2181
      default:
1001
2182
        return ARGP_ERR_UNKNOWN;
1002
2183
      }
1003
 
      return 0;
 
2184
      return errno;
1004
2185
    }
1005
2186
    
1006
2187
    struct argp argp = { .options = options, .parser = parse_opt,
1007
2188
                         .args_doc = "",
1008
2189
                         .doc = "Mandos client -- Get and decrypt"
1009
2190
                         " 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
 
    }
 
2191
    ret = argp_parse(&argp, argc, argv,
 
2192
                     ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
 
2193
    switch(ret){
 
2194
    case 0:
 
2195
      break;
 
2196
    case ENOMEM:
 
2197
    default:
 
2198
      errno = ret;
 
2199
      perror_plus("argp_parse");
 
2200
      exitcode = EX_OSERR;
 
2201
      goto end;
 
2202
    case EINVAL:
 
2203
      exitcode = EX_USAGE;
 
2204
      goto end;
 
2205
    }
 
2206
  }
 
2207
    
 
2208
  {
 
2209
    /* Work around Debian bug #633582:
 
2210
       <http://bugs.debian.org/633582> */
 
2211
    
 
2212
    /* Re-raise privileges */
 
2213
    ret_errno = raise_privileges();
 
2214
    if(ret_errno != 0){
 
2215
      errno = ret_errno;
 
2216
      perror_plus("Failed to raise privileges");
 
2217
    } else {
 
2218
      struct stat st;
 
2219
      
 
2220
      if(strcmp(seckey, PATHDIR "/" SECKEY) == 0){
 
2221
        int seckey_fd = open(seckey, O_RDONLY);
 
2222
        if(seckey_fd == -1){
 
2223
          perror_plus("open");
 
2224
        } else {
 
2225
          ret = (int)TEMP_FAILURE_RETRY(fstat(seckey_fd, &st));
 
2226
          if(ret == -1){
 
2227
            perror_plus("fstat");
 
2228
          } else {
 
2229
            if(S_ISREG(st.st_mode)
 
2230
               and st.st_uid == 0 and st.st_gid == 0){
 
2231
              ret = fchown(seckey_fd, uid, gid);
 
2232
              if(ret == -1){
 
2233
                perror_plus("fchown");
 
2234
              }
 
2235
            }
 
2236
          }
 
2237
          TEMP_FAILURE_RETRY(close(seckey_fd));
 
2238
        }
 
2239
      }
 
2240
    
 
2241
      if(strcmp(pubkey, PATHDIR "/" PUBKEY) == 0){
 
2242
        int pubkey_fd = open(pubkey, O_RDONLY);
 
2243
        if(pubkey_fd == -1){
 
2244
          perror_plus("open");
 
2245
        } else {
 
2246
          ret = (int)TEMP_FAILURE_RETRY(fstat(pubkey_fd, &st));
 
2247
          if(ret == -1){
 
2248
            perror_plus("fstat");
 
2249
          } else {
 
2250
            if(S_ISREG(st.st_mode)
 
2251
               and st.st_uid == 0 and st.st_gid == 0){
 
2252
              ret = fchown(pubkey_fd, uid, gid);
 
2253
              if(ret == -1){
 
2254
                perror_plus("fchown");
 
2255
              }
 
2256
            }
 
2257
          }
 
2258
          TEMP_FAILURE_RETRY(close(pubkey_fd));
 
2259
        }
 
2260
      }
 
2261
    
 
2262
      /* Lower privileges */
 
2263
      ret_errno = lower_privileges();
 
2264
      if(ret_errno != 0){
 
2265
        errno = ret_errno;
 
2266
        perror_plus("Failed to lower privileges");
 
2267
      }
 
2268
    }
 
2269
  }
 
2270
  
 
2271
  /* Remove invalid interface names (except "none") */
 
2272
  {
 
2273
    char *interface = NULL;
 
2274
    while((interface = argz_next(mc.interfaces, mc.interfaces_size,
 
2275
                                 interface))){
 
2276
      if(strcmp(interface, "none") != 0
 
2277
         and if_nametoindex(interface) == 0){
 
2278
        if(interface[0] != '\0'){
 
2279
          fprintf_plus(stderr, "Not using nonexisting interface"
 
2280
                       " \"%s\"\n", interface);
 
2281
        }
 
2282
        argz_delete(&mc.interfaces, &mc.interfaces_size, interface);
 
2283
        interface = NULL;
 
2284
      }
 
2285
    }
 
2286
  }
 
2287
  
 
2288
  /* Run network hooks */
 
2289
  {
 
2290
    if(mc.interfaces != NULL){
 
2291
      interfaces_hooks = malloc(mc.interfaces_size);
 
2292
      if(interfaces_hooks == NULL){
 
2293
        perror_plus("malloc");
 
2294
        goto end;
 
2295
      }
 
2296
      memcpy(interfaces_hooks, mc.interfaces, mc.interfaces_size);
 
2297
      argz_stringify(interfaces_hooks, mc.interfaces_size, (int)',');
 
2298
    }
 
2299
    run_network_hooks("start", interfaces_hooks != NULL ?
 
2300
                      interfaces_hooks : "", delay);
1016
2301
  }
1017
2302
  
1018
2303
  if(not debug){
1023
2308
     from the signal handler */
1024
2309
  /* Initialize the pseudo-RNG for Avahi */
1025
2310
  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;
 
2311
  simple_poll = avahi_simple_poll_new();
 
2312
  if(simple_poll == NULL){
 
2313
    fprintf_plus(stderr,
 
2314
                 "Avahi: Failed to create simple poll object.\n");
 
2315
    exitcode = EX_UNAVAILABLE;
1030
2316
    goto end;
1031
2317
  }
1032
2318
  
1033
2319
  sigemptyset(&sigterm_action.sa_mask);
1034
2320
  ret = sigaddset(&sigterm_action.sa_mask, SIGINT);
1035
2321
  if(ret == -1){
1036
 
    perror("sigaddset");
1037
 
    exitcode = EXIT_FAILURE;
 
2322
    perror_plus("sigaddset");
 
2323
    exitcode = EX_OSERR;
1038
2324
    goto end;
1039
2325
  }
1040
2326
  ret = sigaddset(&sigterm_action.sa_mask, SIGHUP);
1041
2327
  if(ret == -1){
1042
 
    perror("sigaddset");
1043
 
    exitcode = EXIT_FAILURE;
 
2328
    perror_plus("sigaddset");
 
2329
    exitcode = EX_OSERR;
1044
2330
    goto end;
1045
2331
  }
1046
2332
  ret = sigaddset(&sigterm_action.sa_mask, SIGTERM);
1047
2333
  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");
 
2334
    perror_plus("sigaddset");
 
2335
    exitcode = EX_OSERR;
 
2336
    goto end;
 
2337
  }
 
2338
  /* Need to check if the handler is SIG_IGN before handling:
 
2339
     | [[info:libc:Initial Signal Actions]] |
 
2340
     | [[info:libc:Basic Signal Handling]]  |
 
2341
  */
 
2342
  ret = sigaction(SIGINT, NULL, &old_sigterm_action);
 
2343
  if(ret == -1){
 
2344
    perror_plus("sigaction");
 
2345
    return EX_OSERR;
 
2346
  }
 
2347
  if(old_sigterm_action.sa_handler != SIG_IGN){
 
2348
    ret = sigaction(SIGINT, &sigterm_action, NULL);
 
2349
    if(ret == -1){
 
2350
      perror_plus("sigaction");
 
2351
      exitcode = EX_OSERR;
 
2352
      goto end;
 
2353
    }
 
2354
  }
 
2355
  ret = sigaction(SIGHUP, NULL, &old_sigterm_action);
 
2356
  if(ret == -1){
 
2357
    perror_plus("sigaction");
 
2358
    return EX_OSERR;
 
2359
  }
 
2360
  if(old_sigterm_action.sa_handler != SIG_IGN){
 
2361
    ret = sigaction(SIGHUP, &sigterm_action, NULL);
 
2362
    if(ret == -1){
 
2363
      perror_plus("sigaction");
 
2364
      exitcode = EX_OSERR;
 
2365
      goto end;
 
2366
    }
 
2367
  }
 
2368
  ret = sigaction(SIGTERM, NULL, &old_sigterm_action);
 
2369
  if(ret == -1){
 
2370
    perror_plus("sigaction");
 
2371
    return EX_OSERR;
 
2372
  }
 
2373
  if(old_sigterm_action.sa_handler != SIG_IGN){
 
2374
    ret = sigaction(SIGTERM, &sigterm_action, NULL);
 
2375
    if(ret == -1){
 
2376
      perror_plus("sigaction");
 
2377
      exitcode = EX_OSERR;
 
2378
      goto end;
 
2379
    }
 
2380
  }
 
2381
  
 
2382
  /* If no interfaces were specified, make a list */
 
2383
  if(mc.interfaces == NULL){
 
2384
    struct dirent **direntries = NULL;
 
2385
    /* Look for any good interfaces */
 
2386
    ret = scandir(sys_class_net, &direntries, good_interface,
 
2387
                  alphasort);
 
2388
    if(ret >= 1){
 
2389
      /* Add all found interfaces to interfaces list */
 
2390
      for(int i = 0; i < ret; ++i){
 
2391
        ret_errno = argz_add(&mc.interfaces, &mc.interfaces_size,
 
2392
                             direntries[i]->d_name);
 
2393
        if(ret_errno != 0){
 
2394
          errno = ret_errno;
 
2395
          perror_plus("argz_add");
 
2396
          free(direntries[i]);
 
2397
          continue;
 
2398
        }
 
2399
        if(debug){
 
2400
          fprintf_plus(stderr, "Will use interface \"%s\"\n",
 
2401
                       direntries[i]->d_name);
 
2402
        }
 
2403
        free(direntries[i]);
 
2404
      }
 
2405
      free(direntries);
 
2406
    } else {
 
2407
      if(ret == 0){
 
2408
        free(direntries);
 
2409
      }
 
2410
      fprintf_plus(stderr, "Could not find a network interface\n");
 
2411
      exitcode = EXIT_FAILURE;
 
2412
      goto end;
 
2413
    }
 
2414
  }
 
2415
  
 
2416
  /* Bring up interfaces which are down, and remove any "none"s */
 
2417
  {
 
2418
    char *interface = NULL;
 
2419
    while((interface = argz_next(mc.interfaces, mc.interfaces_size,
 
2420
                                 interface))){
 
2421
      /* If interface name is "none", stop bringing up interfaces.
 
2422
         Also remove all instances of "none" from the list */
 
2423
      if(strcmp(interface, "none") == 0){
 
2424
        argz_delete(&mc.interfaces, &mc.interfaces_size,
 
2425
                    interface);
 
2426
        interface = NULL;
 
2427
        while((interface = argz_next(mc.interfaces,
 
2428
                                     mc.interfaces_size, interface))){
 
2429
          if(strcmp(interface, "none") == 0){
 
2430
            argz_delete(&mc.interfaces, &mc.interfaces_size,
 
2431
                        interface);
 
2432
            interface = NULL;
1112
2433
          }
1113
2434
        }
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
2435
        break;
1126
2436
      }
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;
 
2437
      bool interface_was_up = interface_is_up(interface);
 
2438
      errno = bring_up_interface(interface, delay);
 
2439
      if(not interface_was_up){
 
2440
        if(errno != 0){
 
2441
          perror_plus("Failed to bring up interface");
 
2442
        } else {
 
2443
          errno = argz_add(&interfaces_to_take_down,
 
2444
                           &interfaces_to_take_down_size,
 
2445
                           interface);
 
2446
          if(errno != 0){
 
2447
            perror_plus("argz_add");
 
2448
          }
 
2449
        }
 
2450
      }
 
2451
    }
 
2452
    if(debug and (interfaces_to_take_down == NULL)){
 
2453
      fprintf_plus(stderr, "No interfaces were brought up\n");
 
2454
    }
 
2455
  }
 
2456
  
 
2457
  /* If we only got one interface, explicitly use only that one */
 
2458
  if(argz_count(mc.interfaces, mc.interfaces_size) == 1){
 
2459
    if(debug){
 
2460
      fprintf_plus(stderr, "Using only interface \"%s\"\n",
 
2461
                   mc.interfaces);
 
2462
    }
 
2463
    if_index = (AvahiIfIndex)if_nametoindex(mc.interfaces);
 
2464
  }
 
2465
  
 
2466
  if(quit_now){
 
2467
    goto end;
 
2468
  }
 
2469
  
 
2470
  ret = init_gnutls_global(pubkey, seckey, &mc);
 
2471
  if(ret == -1){
 
2472
    fprintf_plus(stderr, "init_gnutls_global failed\n");
 
2473
    exitcode = EX_UNAVAILABLE;
1168
2474
    goto end;
1169
2475
  } else {
1170
2476
    gnutls_initialized = true;
1171
2477
  }
1172
2478
  
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;
 
2479
  if(quit_now){
 
2480
    goto end;
 
2481
  }
 
2482
  
 
2483
  /* Try /run/tmp before /tmp */
 
2484
  tempdir = mkdtemp(run_tempdir);
 
2485
  if(tempdir == NULL and errno == ENOENT){
 
2486
      if(debug){
 
2487
        fprintf_plus(stderr, "Tempdir %s did not work, trying %s\n",
 
2488
                     run_tempdir, old_tempdir);
 
2489
      }
 
2490
      tempdir = mkdtemp(old_tempdir);
 
2491
  }
 
2492
  if(tempdir == NULL){
 
2493
    perror_plus("mkdtemp");
 
2494
    goto end;
 
2495
  }
 
2496
  
 
2497
  if(quit_now){
 
2498
    goto end;
 
2499
  }
 
2500
  
 
2501
  if(not init_gpgme(pubkey, seckey, tempdir, &mc)){
 
2502
    fprintf_plus(stderr, "init_gpgme failed\n");
 
2503
    exitcode = EX_UNAVAILABLE;
1182
2504
    goto end;
1183
2505
  } else {
1184
2506
    gpgme_initialized = true;
1185
2507
  }
1186
2508
  
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
 
    }
 
2509
  if(quit_now){
 
2510
    goto end;
1194
2511
  }
1195
2512
  
1196
2513
  if(connect_to != NULL){
1197
2514
    /* Connect directly, do not use Zeroconf */
1198
2515
    /* (Mainly meant for debugging) */
1199
2516
    char *address = strrchr(connect_to, ':');
 
2517
    
1200
2518
    if(address == NULL){
1201
 
      fprintf(stderr, "No colon in address\n");
1202
 
      exitcode = EXIT_FAILURE;
1203
 
      goto end;
1204
 
    }
1205
 
    uint16_t port;
 
2519
      fprintf_plus(stderr, "No colon in address\n");
 
2520
      exitcode = EX_USAGE;
 
2521
      goto end;
 
2522
    }
 
2523
    
 
2524
    if(quit_now){
 
2525
      goto end;
 
2526
    }
 
2527
    
 
2528
    in_port_t port;
1206
2529
    errno = 0;
1207
2530
    tmpmax = strtoimax(address+1, &tmp, 10);
1208
2531
    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;
 
2532
       or tmpmax != (in_port_t)tmpmax){
 
2533
      fprintf_plus(stderr, "Bad port number\n");
 
2534
      exitcode = EX_USAGE;
 
2535
      goto end;
 
2536
    }
 
2537
    
 
2538
    if(quit_now){
 
2539
      goto end;
 
2540
    }
 
2541
    
 
2542
    port = (in_port_t)tmpmax;
1215
2543
    *address = '\0';
1216
 
    address = connect_to;
1217
2544
    /* Colon in address indicates IPv6 */
1218
2545
    int af;
1219
 
    if(strchr(address, ':') != NULL){
 
2546
    if(strchr(connect_to, ':') != NULL){
1220
2547
      af = AF_INET6;
 
2548
      /* Accept [] around IPv6 address - see RFC 5952 */
 
2549
      if(connect_to[0] == '[' and address[-1] == ']')
 
2550
        {
 
2551
          connect_to++;
 
2552
          address[-1] = '\0';
 
2553
        }
1221
2554
    } else {
1222
2555
      af = AF_INET;
1223
2556
    }
1224
 
    ret = start_mandos_communication(address, port, if_index, af);
1225
 
    if(ret < 0){
1226
 
      exitcode = EXIT_FAILURE;
1227
 
    } else {
 
2557
    address = connect_to;
 
2558
    
 
2559
    if(quit_now){
 
2560
      goto end;
 
2561
    }
 
2562
    
 
2563
    while(not quit_now){
 
2564
      ret = start_mandos_communication(address, port, if_index, af,
 
2565
                                       &mc);
 
2566
      if(quit_now or ret == 0){
 
2567
        break;
 
2568
      }
 
2569
      if(debug){
 
2570
        fprintf_plus(stderr, "Retrying in %d seconds\n",
 
2571
                     (int)retry_interval);
 
2572
      }
 
2573
      sleep((unsigned int)retry_interval);
 
2574
    }
 
2575
    
 
2576
    if(not quit_now){
1228
2577
      exitcode = EXIT_SUCCESS;
1229
2578
    }
1230
 
    goto end;
1231
 
  }
1232
2579
    
 
2580
    goto end;
 
2581
  }
 
2582
  
 
2583
  if(quit_now){
 
2584
    goto end;
 
2585
  }
 
2586
  
1233
2587
  {
1234
2588
    AvahiServerConfig config;
1235
2589
    /* Do not publish any local Zeroconf records */
1240
2594
    config.publish_domain = 0;
1241
2595
    
1242
2596
    /* Allocate a new server */
1243
 
    mc.server = avahi_server_new(avahi_simple_poll_get
1244
 
                                 (mc.simple_poll), &config, NULL,
1245
 
                                 NULL, &error);
 
2597
    mc.server = avahi_server_new(avahi_simple_poll_get(simple_poll),
 
2598
                                 &config, NULL, NULL, &ret_errno);
1246
2599
    
1247
2600
    /* Free the Avahi configuration data */
1248
2601
    avahi_server_config_free(&config);
1250
2603
  
1251
2604
  /* Check if creating the Avahi server object succeeded */
1252
2605
  if(mc.server == NULL){
1253
 
    fprintf(stderr, "Failed to create Avahi server: %s\n",
1254
 
            avahi_strerror(error));
1255
 
    exitcode = EXIT_FAILURE;
 
2606
    fprintf_plus(stderr, "Failed to create Avahi server: %s\n",
 
2607
                 avahi_strerror(ret_errno));
 
2608
    exitcode = EX_UNAVAILABLE;
 
2609
    goto end;
 
2610
  }
 
2611
  
 
2612
  if(quit_now){
1256
2613
    goto end;
1257
2614
  }
1258
2615
  
1259
2616
  /* Create the Avahi service browser */
1260
2617
  sb = avahi_s_service_browser_new(mc.server, if_index,
1261
2618
                                   AVAHI_PROTO_UNSPEC, "_mandos._tcp",
1262
 
                                   NULL, 0, browse_callback, NULL);
 
2619
                                   NULL, 0, browse_callback,
 
2620
                                   (void *)&mc);
1263
2621
  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;
 
2622
    fprintf_plus(stderr, "Failed to create service browser: %s\n",
 
2623
                 avahi_strerror(avahi_server_errno(mc.server)));
 
2624
    exitcode = EX_UNAVAILABLE;
 
2625
    goto end;
 
2626
  }
 
2627
  
 
2628
  if(quit_now){
1267
2629
    goto end;
1268
2630
  }
1269
2631
  
1270
2632
  /* Run the main loop */
1271
2633
  
1272
2634
  if(debug){
1273
 
    fprintf(stderr, "Starting Avahi loop search\n");
 
2635
    fprintf_plus(stderr, "Starting Avahi loop search\n");
1274
2636
  }
1275
2637
  
1276
 
  avahi_simple_poll_loop(mc.simple_poll);
 
2638
  ret = avahi_loop_with_timeout(simple_poll,
 
2639
                                (int)(retry_interval * 1000), &mc);
 
2640
  if(debug){
 
2641
    fprintf_plus(stderr, "avahi_loop_with_timeout exited %s\n",
 
2642
                 (ret == 0) ? "successfully" : "with error");
 
2643
  }
1277
2644
  
1278
2645
 end:
1279
2646
  
1280
2647
  if(debug){
1281
 
    fprintf(stderr, "%s exiting\n", argv[0]);
 
2648
    fprintf_plus(stderr, "%s exiting\n", argv[0]);
1282
2649
  }
1283
2650
  
1284
2651
  /* Cleanup things */
 
2652
  free(mc.interfaces);
 
2653
  
1285
2654
  if(sb != NULL)
1286
2655
    avahi_s_service_browser_free(sb);
1287
2656
  
1288
2657
  if(mc.server != NULL)
1289
2658
    avahi_server_free(mc.server);
1290
2659
  
1291
 
  if(mc.simple_poll != NULL)
1292
 
    avahi_simple_poll_free(mc.simple_poll);
 
2660
  if(simple_poll != NULL)
 
2661
    avahi_simple_poll_free(simple_poll);
1293
2662
  
1294
2663
  if(gnutls_initialized){
1295
2664
    gnutls_certificate_free_credentials(mc.cred);
1301
2670
    gpgme_release(mc.ctx);
1302
2671
  }
1303
2672
  
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
 
    }
 
2673
  /* Cleans up the circular linked list of Mandos servers the client
 
2674
     has seen */
 
2675
  if(mc.current_server != NULL){
 
2676
    mc.current_server->prev->next = NULL;
 
2677
    while(mc.current_server != NULL){
 
2678
      server *next = mc.current_server->next;
 
2679
#ifdef __GNUC__
 
2680
#pragma GCC diagnostic push
 
2681
#pragma GCC diagnostic ignored "-Wcast-qual"
 
2682
#endif
 
2683
      free((char *)(mc.current_server->ip));
 
2684
#ifdef __GNUC__
 
2685
#pragma GCC diagnostic pop
 
2686
#endif
 
2687
      free(mc.current_server);
 
2688
      mc.current_server = next;
 
2689
    }
 
2690
  }
 
2691
  
 
2692
  /* Re-raise privileges */
 
2693
  {
 
2694
    ret_errno = raise_privileges();
 
2695
    if(ret_errno != 0){
 
2696
      errno = ret_errno;
 
2697
      perror_plus("Failed to raise privileges");
 
2698
    } else {
 
2699
      
 
2700
      /* Run network hooks */
 
2701
      run_network_hooks("stop", interfaces_hooks != NULL ?
 
2702
                        interfaces_hooks : "", delay);
 
2703
      
 
2704
      /* Take down the network interfaces which were brought up */
 
2705
      {
 
2706
        char *interface = NULL;
 
2707
        while((interface=argz_next(interfaces_to_take_down,
 
2708
                                   interfaces_to_take_down_size,
 
2709
                                   interface))){
 
2710
          ret_errno = take_down_interface(interface);
 
2711
          if(ret_errno != 0){
 
2712
            errno = ret_errno;
 
2713
            perror_plus("Failed to take down interface");
 
2714
          }
 
2715
        }
 
2716
        if(debug and (interfaces_to_take_down == NULL)){
 
2717
          fprintf_plus(stderr, "No interfaces needed to be taken"
 
2718
                       " down\n");
 
2719
        }
 
2720
      }
 
2721
    }
 
2722
    
 
2723
    ret_errno = lower_privileges_permanently();
 
2724
    if(ret_errno != 0){
 
2725
      errno = ret_errno;
 
2726
      perror_plus("Failed to lower privileges permanently");
 
2727
    }
 
2728
  }
 
2729
  
 
2730
  free(interfaces_to_take_down);
 
2731
  free(interfaces_hooks);
 
2732
  
 
2733
  /* Removes the GPGME temp directory and all files inside */
 
2734
  if(tempdir != NULL){
 
2735
    struct dirent **direntries = NULL;
 
2736
    int tempdir_fd = (int)TEMP_FAILURE_RETRY(open(tempdir, O_RDONLY |
 
2737
                                                  O_NOFOLLOW));
 
2738
    if(tempdir_fd == -1){
 
2739
      perror_plus("open");
 
2740
    } else {
 
2741
#ifdef __GLIBC__
 
2742
#if __GLIBC_PREREQ(2, 15)
 
2743
      int numentries = scandirat(tempdir_fd, ".", &direntries,
 
2744
                                 notdotentries, alphasort);
 
2745
#else  /* not __GLIBC_PREREQ(2, 15) */
 
2746
      int numentries = scandir(tempdir, &direntries, notdotentries,
 
2747
                               alphasort);
 
2748
#endif  /* not __GLIBC_PREREQ(2, 15) */
 
2749
#else   /* not __GLIBC__ */
 
2750
      int numentries = scandir(tempdir, &direntries, notdotentries,
 
2751
                               alphasort);
 
2752
#endif  /* not __GLIBC__ */
 
2753
      if(numentries >= 0){
 
2754
        for(int i = 0; i < numentries; i++){
 
2755
          ret = unlinkat(tempdir_fd, direntries[i]->d_name, 0);
 
2756
          if(ret == -1){
 
2757
            fprintf_plus(stderr, "unlinkat(open(\"%s\", O_RDONLY),"
 
2758
                         " \"%s\", 0): %s\n", tempdir,
 
2759
                         direntries[i]->d_name, strerror(errno));
 
2760
          }
 
2761
          free(direntries[i]);
 
2762
        }
 
2763
        
 
2764
        /* need to clean even if 0 because man page doesn't specify */
 
2765
        free(direntries);
 
2766
        if(numentries == -1){
 
2767
          perror_plus("scandir");
 
2768
        }
 
2769
        ret = rmdir(tempdir);
 
2770
        if(ret == -1 and errno != ENOENT){
 
2771
          perror_plus("rmdir");
 
2772
        }
 
2773
      }
 
2774
      TEMP_FAILURE_RETRY(close(tempdir_fd));
 
2775
    }
 
2776
  }
 
2777
  
 
2778
  if(quit_now){
 
2779
    sigemptyset(&old_sigterm_action.sa_mask);
 
2780
    old_sigterm_action.sa_handler = SIG_DFL;
 
2781
    ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received,
 
2782
                                            &old_sigterm_action,
 
2783
                                            NULL));
 
2784
    if(ret == -1){
 
2785
      perror_plus("sigaction");
 
2786
    }
 
2787
    do {
 
2788
      ret = raise(signal_received);
 
2789
    } while(ret != 0 and errno == EINTR);
 
2790
    if(ret != 0){
 
2791
      perror_plus("raise");
 
2792
      abort();
 
2793
    }
 
2794
    TEMP_FAILURE_RETRY(pause());
1364
2795
  }
1365
2796
  
1366
2797
  return exitcode;