/mandos/trunk

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

« back to all changes in this revision

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

  • Committer: Teddy Hogeborn
  • Date: 2018-04-01 22:08:50 UTC
  • Revision ID: teddy@recompile.se-20180401220850-11kc7596lknn4piy
Check for and report GPGME key import errors

* plugins.d/mandos-client.c (init_gpgme/import_key): After
  gpgme_op_import(), call gpgme_op_import_result() to see if it
  worked.  If not, print all available information.

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