/mandos/trunk

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

« back to all changes in this revision

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

  • Committer: Teddy Hogeborn
  • Date: 2012-06-17 02:30:59 UTC
  • Revision ID: teddy@recompile.se-20120617023059-em4nfnxg1tsn64xj
* plugins.d/mandos-client (start_mandos_communication): Bug fix; skip
                                                        non-specified
                                                        interfaces.
  (main): Use lower_privileges() consistently.  Bug fix: Don't remove
          "none" from list of interfaces.  Make --interface=none work
          again by not bringing up interfaces specified after "none".
* plugins.d/mandos-client.xml (OPTIONS): Document new meaning of
                                         specifying --interface=none
                                         together with other
                                         interface names,

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