/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/mandosclient.c

  • Committer: Teddy Hogeborn
  • Date: 2008-07-22 06:23:29 UTC
  • Revision ID: teddy@fukt.bsnet.se-20080722062329-jaex5y5mslf6sqjx
* mandos-clients.conf ([DEFAULT]): New section.

* plugins.d/mandosclient.c (start_mandos_communication): Only print if
                                                         debugging.
                                                         Print server
                                                         name.
                                                         Bug fix: Loop
                                                         until suc-
                                                         cess.

* server.py (serverName): Set via option, not globally.
  (Client.__init__): Removed argument "options".  Require "timeout"
                     and "interval" arguments.
  (tcp_handler.handle): Set "priority" from self.server.options.
  (main): Removed "--timeout" and "--interval" options.  New options
          "--priority" and "--servicename".  Add defaults for
          "timeout" and "interval".  Set "serviceName" from options.
          Do not pass "options" to "Client()".

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