/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: 2018-04-01 19:31:31 UTC
  • Revision ID: teddy@recompile.se-20180401193131-w8but5r9ryne9et4
Print a boolean more readably in error message output

* plugins.d/mandos-client.c (pgp_packet_decrypt): If decryption fails,
  print wrong_key_usage as "Yes" or "No", not a number.

Show diffs side-by-side

added added

removed removed

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