/mandos/trunk

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

« back to all changes in this revision

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

  • Committer: Teddy Hogeborn
  • Date: 2015-05-31 15:56:58 UTC
  • Revision ID: teddy@recompile.se-20150531155658-l7znu7zlqr2dmuwd
mandos: Generate better messages in exceptions.

mandos (ProxyClient.__init__): Include fingerprint in KeyError().
(rfc3339_duration_to_delta): Include duration in ValueError().

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