/mandos/trunk

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

« back to all changes in this revision

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

  • Committer: Teddy Hogeborn
  • Date: 2012-06-16 23:25:46 UTC
  • Revision ID: teddy@recompile.se-20120616232546-dcrkmnhd4yhq6mvd
* plugins.d/mandos-client (mandos_context): Moved to inside "main()".
                                            All users changed.
  (add_server): Take new "current_server" argument; all callers
                changed.
  (init_gpgme, pgp_packet_decrypt, init_gnutls_global,
  init_gnutls_session, start_mandos_communication,
  avahi_loop_with_timeout): Take new "mc" argument"; all callers
                            changed.
  (resolve_callback, browse_callback): Handle void pointer userdata as
                                       "mc", a pointer to
                                       mandos_context.  All callers
                                       changed.
  (main): New "mc" variable.

Show diffs side-by-side

added added

removed removed

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