/mandos/trunk

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

« back to all changes in this revision

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

  • Committer: Teddy Hogeborn
  • Date: 2018-08-15 09:13:36 UTC
  • Revision ID: teddy@recompile.se-20180815091336-fxwk6vbop0m8xyur
Linking flags correction

* Makefile (CFLAGS): Remove "$(GNUTLS_CFLAGS) $(AVAHI_CFLAGS)
                     $(GPGME_CFLAGS)", since these libraries are not
                     used by all programs.
  (plugins.d/mandos-client): Add "$(GNUTLS_CFLAGS) $(AVAHI_CFLAGS)
                             $(GPGME_CFLAGS)".

Show diffs side-by-side

added added

removed removed

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