/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: 2014-03-23 19:24:40 UTC
  • Revision ID: teddy@recompile.se-20140323192440-d71iiqxebsxf9u2v
Update GCC warning flags and function attributes to GCC 4.7.

* Makefile (WARN): Update to include almost all warning flags.
* plugin-runner.c (getplugin, add_to_char_array, add_argument,
                   add_environment, set_cloexec_flag,
                   print_out_password): Add attribute
                                        "warn_unused_result".
  (main/parse_opt): Bug fix: Add error checking to --global-env,
                    --env-for, --plugin-dir, and --config-file, and
                    make sure errno does not "leak" from unrelated
                    functions.
* plugins.d/mandos-client.c
  (fprintf_plus, debuggnutls, resolve_callback): Add "nonnull"
                                                 attribute.
  (incbuffer, add_server, init_gpgme): Add "nonnull" and
                                       "warn_unused_result"
                                       attributes.
  (pgp_packet_decrypt, init_gnutls_global): - '' -
  (init_gnutls_session start_mandos_communication, get_flags): - '' -
  (good_flags, good_interface, interface_is_up): - '' -
  (interface_is_running, runnable_hook): - '' -
  (avahi_loop_with_timeout, bring_up_interface): : - '' -
  (safer_gnutls_strerror): Add "warn_unused_result" attribute.
  (notdotentries): Set "nonnull", "pure", and "warn_unused_result"
                   attributes.
  (raise_privileges, raise_privileges_permanently, lower_privileges,
  lower_privileges_permanently): Set "warn_unused_result" attribute.
  (run_network_hooks): Exit child process if it fails to do anything
                       it needs to do.  Make explicit cast to double
                       when passing float value to asprintf().  Change
                       return type to void - all callers changed.
  (bring_up_interface): Move variables "sd", "ret_errno", and
                        "ret_setflags" to innermost scope.  Bug fix:
                        Fail if could not get interface flags also in
                        non-debug mode, and restore old errno
                        correctly.  Print message if could not raise
                        (or later lower) privileges.
  (take_down_interface): Bug fix: When failing because it could not
                         get interface flags, restore old errno
                         correctly.  Print message if it could not
                         raise (or later lower) privileges.
  (main): Complain if failed to raise or lower privileges.  Only run
          network hooks or lower privileges if raising privileges was
          successful.

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