/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/password-request.c

  • Committer: Teddy Hogeborn
  • Date: 2008-08-16 03:29:08 UTC
  • Revision ID: teddy@fukt.bsnet.se-20080816032908-ihw7c05r2mnyk389
Add feature to specify custom environment variables for plugins.

* plugin-runner.c (plugin): New members "environ" and "envc" to
                            contain possible custom environment.
  (getplugin): Return NULL on failure instead of doing exit(); all
               callers changed.
  (add_to_char_array): New helper function for "add_argument" and
                       "add_environment".
  (addargument): Renamed to "add_argument".  Return bool.  Call
                 "add_to_char_array" to actually do things.
  (add_environment): New; analogous to "add_argument".
  (addcustomargument): Renamed to "add_to_argv" to avoid confusion
                       with "add_argument".
  (main): New options "--global-envs" and "--envs-for" to specify
          custom environment for plugins.  Print environment for
          plugins in debug mode.  Use asprintf instead of strcpy and
          strcat.  Use execve() for plugins with custom environments.
          Free environment for plugin when freeing plugin list.

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