/mandos/release

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

« back to all changes in this revision

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

  • Committer: Teddy Hogeborn
  • Date: 2014-03-09 03:02:43 UTC
  • mto: (237.7.272 trunk)
  • mto: This revision was merged to the branch mainline in revision 311.
  • Revision ID: teddy@recompile.se-20140309030243-0gc6l2yuqbzgyuyt
Use getnameinfo() instead of inet_ntop() in mandos-client.

* plugins.d/mandos-client.c (start_mandos_communication): Use
                                                          getnameinfo()
                                                          instead of
                                                          inet_ntop().

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