/mandos/trunk

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

« back to all changes in this revision

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

  • Committer: Teddy Hogeborn
  • Date: 2015-01-25 00:02:51 UTC
  • Revision ID: teddy@recompile.se-20150125000251-j2bw50gfq9smqyxe
mandos.xml (SEE ALSO): Update links.

Update link to GnuPG home page, change reference from TLS 1.1 to TLS
1.2, and change to latest RFC for using OpenPGP keys with TLS (and use
its correct title).

Show diffs side-by-side

added added

removed removed

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