/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: 2015-07-20 00:06:05 UTC
  • Revision ID: teddy@recompile.se-20150720000605-244crrgqx254i3bs
Don't use TEMP_FAILURE_RETRY around close().

* plugin-runner.c: Don't use TEMP_FAILURE_RETRY around close().
* plugins.d/mandos-client.c: - '' -
* plugins.d/usplash.c: - '' -

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