/mandos/release

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

« 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
  • mto: (237.7.272 trunk)
  • mto: This revision was merged to the branch mainline in revision 311.
  • 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>
70
 
 
71
 
#ifndef CERT_ROOT
72
 
#define CERT_ROOT "/conf/conf.d/cryptkeyreq/"
73
 
#endif
74
 
#define CERTFILE CERT_ROOT "openpgp-client.txt"
75
 
#define KEYFILE CERT_ROOT "openpgp-client-key.txt"
 
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_* */
 
129
 
76
130
#define BUFFER_SIZE 256
77
 
#define DH_BITS 1024
 
131
 
 
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"
78
136
 
79
137
bool debug = false;
80
 
 
 
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 */
81
159
typedef struct {
82
 
  gnutls_session_t session;
 
160
  AvahiServer *server;
83
161
  gnutls_certificate_credentials_t cred;
 
162
  unsigned int dh_bits;
84
163
  gnutls_dh_params_t dh_params;
85
 
} encrypted_session;
86
 
 
87
 
 
88
 
ssize_t pgp_packet_decrypt (char *packet, size_t packet_size,
89
 
                            char **new_packet, const char *homedir){
90
 
  gpgme_data_t dh_crypto, dh_plain;
 
164
  const char *priority;
91
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){
92
263
  gpgme_error_t rc;
93
 
  ssize_t ret;
94
 
  ssize_t new_packet_capacity = 0;
95
 
  ssize_t new_packet_length = 0;
96
264
  gpgme_engine_info_t engine_info;
97
 
 
98
 
  if (debug){
99
 
    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");
100
304
  }
101
305
  
102
306
  /* Init GPGME */
103
307
  gpgme_check_version(NULL);
104
 
  gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
 
308
  rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
 
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;
 
313
  }
105
314
  
106
 
  /* Set GPGME home directory */
107
 
  rc = gpgme_get_engine_info (&engine_info);
108
 
  if (rc != GPG_ERR_NO_ERROR){
109
 
    fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
110
 
            gpgme_strsource(rc), gpgme_strerror(rc));
111
 
    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;
112
321
  }
113
322
  while(engine_info != NULL){
114
323
    if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
115
324
      gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
116
 
                            engine_info->file_name, homedir);
 
325
                            engine_info->file_name, tempdir);
117
326
      break;
118
327
    }
119
328
    engine_info = engine_info->next;
120
329
  }
121
330
  if(engine_info == NULL){
122
 
    fprintf(stderr, "Could not set home dir to %s\n", homedir);
123
 
    return -1;
124
 
  }
125
 
  
126
 
  /* Create new GPGME data buffer from packet buffer */
127
 
  rc = gpgme_data_new_from_mem(&dh_crypto, packet, packet_size, 0);
128
 
  if (rc != GPG_ERR_NO_ERROR){
129
 
    fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
130
 
            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));
131
377
    return -1;
132
378
  }
133
379
  
134
380
  /* Create new empty GPGME data buffer for the plaintext */
135
381
  rc = gpgme_data_new(&dh_plain);
136
 
  if (rc != GPG_ERR_NO_ERROR){
137
 
    fprintf(stderr, "bad gpgme_data_new: %s: %s\n",
138
 
            gpgme_strsource(rc), gpgme_strerror(rc));
139
 
    return -1;
140
 
  }
141
 
  
142
 
  /* Create new GPGME "context" */
143
 
  rc = gpgme_new(&ctx);
144
 
  if (rc != GPG_ERR_NO_ERROR){
145
 
    fprintf(stderr, "bad gpgme_new: %s: %s\n",
146
 
            gpgme_strsource(rc), gpgme_strerror(rc));
147
 
    return -1;
148
 
  }
149
 
  
150
 
  /* Decrypt data from the FILE pointer to the plaintext data
151
 
     buffer */
152
 
  rc = gpgme_op_decrypt(ctx, dh_crypto, dh_plain);
153
 
  if (rc != GPG_ERR_NO_ERROR){
154
 
    fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
155
 
            gpgme_strsource(rc), gpgme_strerror(rc));
156
 
    return -1;
157
 
  }
158
 
 
159
 
  if(debug){
160
 
    fprintf(stderr, "Decryption of OpenPGP packet succeeded\n");
161
 
  }
162
 
 
163
 
  if (debug){
164
 
    gpgme_decrypt_result_t result;
165
 
    result = gpgme_op_decrypt_result(ctx);
166
 
    if (result == NULL){
167
 
      fprintf(stderr, "gpgme_op_decrypt_result failed\n");
168
 
    } else {
169
 
      fprintf(stderr, "Unsupported algorithm: %s\n",
170
 
              result->unsupported_algorithm);
171
 
      fprintf(stderr, "Wrong key usage: %d\n",
172
 
              result->wrong_key_usage);
173
 
      if(result->file_name != NULL){
174
 
        fprintf(stderr, "File name: %s\n", result->file_name);
175
 
      }
176
 
      gpgme_recipient_t recipient;
177
 
      recipient = result->recipients;
178
 
      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;
179
412
        while(recipient != NULL){
180
 
          fprintf(stderr, "Public key algorithm: %s\n",
181
 
                  gpgme_pubkey_algo_name(recipient->pubkey_algo));
182
 
          fprintf(stderr, "Key ID: %s\n", recipient->keyid);
183
 
          fprintf(stderr, "Secret key available: %s\n",
184
 
                  recipient->status == GPG_ERR_NO_SECKEY
185
 
                  ? "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");
186
420
          recipient = recipient->next;
187
421
        }
188
422
      }
189
423
    }
 
424
    goto decrypt_end;
190
425
  }
191
426
  
192
 
  /* Delete the GPGME FILE pointer cryptotext data buffer */
193
 
  gpgme_data_release(dh_crypto);
 
427
  if(debug){
 
428
    fprintf_plus(stderr, "Decryption of OpenPGP data succeeded\n");
 
429
  }
194
430
  
195
431
  /* Seek back to the beginning of the GPGME plaintext data buffer */
196
 
  gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET);
197
 
 
198
 
  *new_packet = 0;
 
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;
 
436
  }
 
437
  
 
438
  *plaintext = NULL;
199
439
  while(true){
200
 
    if (new_packet_length + BUFFER_SIZE > new_packet_capacity){
201
 
      *new_packet = realloc(*new_packet,
202
 
                            (unsigned int)new_packet_capacity
203
 
                            + BUFFER_SIZE);
204
 
      if (*new_packet == NULL){
205
 
        perror("realloc");
206
 
        return -1;
207
 
      }
208
 
      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;
209
447
    }
210
448
    
211
 
    ret = gpgme_data_read(dh_plain, *new_packet + new_packet_length,
 
449
    ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
212
450
                          BUFFER_SIZE);
213
451
    /* Print the data, if any */
214
 
    if (ret == 0){
 
452
    if(ret == 0){
 
453
      /* EOF */
215
454
      break;
216
455
    }
217
456
    if(ret < 0){
218
 
      perror("gpgme_data_read");
219
 
      return -1;
220
 
    }
221
 
    new_packet_length += ret;
222
 
  }
223
 
 
224
 
  /* FIXME: check characters before printing to screen so to not print
225
 
     terminal control characters */
226
 
  /*   if(debug){ */
227
 
  /*     fprintf(stderr, "decrypted password is: "); */
228
 
  /*     fwrite(*new_packet, 1, new_packet_length, stderr); */
229
 
  /*     fprintf(stderr, "\n"); */
230
 
  /*   } */
 
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);
231
476
  
232
477
  /* Delete the GPGME plaintext data buffer */
233
478
  gpgme_data_release(dh_plain);
234
 
  return new_packet_length;
 
479
  return plaintext_length;
235
480
}
236
481
 
237
 
static const char * safer_gnutls_strerror (int value) {
238
 
  const char *ret = gnutls_strerror (value);
239
 
  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)
240
486
    ret = "(unknown)";
241
487
  return ret;
242
488
}
243
489
 
244
 
void debuggnutls(__attribute__((unused)) int level,
245
 
                 const char* string){
246
 
  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);
247
495
}
248
496
 
249
 
int initgnutls(encrypted_session *es){
250
 
  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){
251
501
  int ret;
252
502
  
253
503
  if(debug){
254
 
    fprintf(stderr, "Initializing GnuTLS\n");
 
504
    fprintf_plus(stderr, "Initializing GnuTLS\n");
255
505
  }
256
506
  
257
 
  if ((ret = gnutls_global_init ())
258
 
      != GNUTLS_E_SUCCESS) {
259
 
    fprintf (stderr, "global_init: %s\n", safer_gnutls_strerror(ret));
 
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));
260
511
    return -1;
261
512
  }
262
 
 
263
 
  if (debug){
 
513
  
 
514
  if(debug){
 
515
    /* "Use a log level over 10 to enable all debugging options."
 
516
     * - GnuTLS manual
 
517
     */
264
518
    gnutls_global_set_log_level(11);
265
519
    gnutls_global_set_log_function(debuggnutls);
266
520
  }
267
521
  
268
 
  /* openpgp credentials */
269
 
  if ((ret = gnutls_certificate_allocate_credentials (&es->cred))
270
 
      != GNUTLS_E_SUCCESS) {
271
 
    fprintf (stderr, "memory error: %s\n",
272
 
             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();
273
528
    return -1;
274
529
  }
275
530
  
276
531
  if(debug){
277
 
    fprintf(stderr, "Attempting to use OpenPGP certificate %s"
278
 
            " and keyfile %s as GnuTLS credentials\n", CERTFILE,
279
 
            KEYFILE);
 
532
    fprintf_plus(stderr, "Attempting to use OpenPGP public key %s and"
 
533
                 " secret key %s as GnuTLS credentials\n",
 
534
                 pubkeyfilename,
 
535
                 seckeyfilename);
280
536
  }
281
537
  
282
538
  ret = gnutls_certificate_set_openpgp_key_file
283
 
    (es->cred, CERTFILE, KEYFILE, GNUTLS_OPENPGP_FMT_BASE64);
284
 
  if (ret != GNUTLS_E_SUCCESS) {
285
 
    fprintf
286
 
      (stderr, "Error[%d] while reading the OpenPGP key pair ('%s',"
287
 
       " '%s')\n",
288
 
       ret, CERTFILE, KEYFILE);
289
 
    fprintf(stdout, "The Error is: %s\n",
290
 
            safer_gnutls_strerror(ret));
291
 
    return -1;
292
 
  }
293
 
  
294
 
  //GnuTLS server initialization
295
 
  if ((ret = gnutls_dh_params_init (&es->dh_params))
296
 
      != GNUTLS_E_SUCCESS) {
297
 
    fprintf (stderr, "Error in dh parameter initialization: %s\n",
298
 
             safer_gnutls_strerror(ret));
299
 
    return -1;
300
 
  }
301
 
  
302
 
  if ((ret = gnutls_dh_params_generate2 (es->dh_params, DH_BITS))
303
 
      != GNUTLS_E_SUCCESS) {
304
 
    fprintf (stderr, "Error in prime generation: %s\n",
305
 
             safer_gnutls_strerror(ret));
306
 
    return -1;
307
 
  }
308
 
  
309
 
  gnutls_certificate_set_dh_params (es->cred, es->dh_params);
310
 
  
311
 
  // GnuTLS session creation
312
 
  if ((ret = gnutls_init (&es->session, GNUTLS_SERVER))
313
 
      != GNUTLS_E_SUCCESS){
314
 
    fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
315
 
            safer_gnutls_strerror(ret));
316
 
  }
317
 
  
318
 
  if ((ret = gnutls_priority_set_direct (es->session, "NORMAL", &err))
319
 
      != GNUTLS_E_SUCCESS) {
320
 
    fprintf(stderr, "Syntax error at: %s\n", err);
321
 
    fprintf(stderr, "GnuTLS error: %s\n",
322
 
            safer_gnutls_strerror(ret));
323
 
    return -1;
324
 
  }
325
 
  
326
 
  if ((ret = gnutls_credentials_set
327
 
       (es->session, GNUTLS_CRD_CERTIFICATE, es->cred))
328
 
      != GNUTLS_E_SUCCESS) {
329
 
    fprintf(stderr, "Error setting a credentials set: %s\n",
330
 
            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);
331
624
    return -1;
332
625
  }
333
626
  
334
627
  /* ignore client certificate if any. */
335
 
  gnutls_certificate_server_set_request (es->session,
336
 
                                         GNUTLS_CERT_IGNORE);
 
628
  gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE);
337
629
  
338
 
  gnutls_dh_set_prime_bits (es->session, DH_BITS);
 
630
  gnutls_dh_set_prime_bits(*session, mc->dh_bits);
339
631
  
340
632
  return 0;
341
633
}
342
634
 
343
 
void empty_log(__attribute__((unused)) AvahiLogLevel level,
344
 
               __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){}
345
638
 
346
 
int start_mandos_communication(const char *ip, uint16_t port,
347
 
                               unsigned int if_index){
348
 
  int ret, tcp_sd;
349
 
  struct sockaddr_in6 to;
350
 
  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;
351
647
  char *buffer = NULL;
352
 
  char *decrypted_buffer;
 
648
  char *decrypted_buffer = NULL;
353
649
  size_t buffer_length = 0;
354
650
  size_t buffer_capacity = 0;
355
 
  ssize_t decrypted_buffer_size;
356
 
  size_t written = 0;
357
 
  int retval = 0;
358
 
  char interface[IF_NAMESIZE];
359
 
  
360
 
  if(debug){
361
 
    fprintf(stderr, "Setting up a tcp connection to %s\n", ip);
362
 
  }
363
 
  
364
 
  tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
365
 
  if(tcp_sd < 0) {
366
 
    perror("socket");
367
 
    return -1;
368
 
  }
369
 
  
370
 
  if(if_indextoname(if_index, interface) == NULL){
371
 
    if(debug){
372
 
      perror("if_indextoname");
373
 
    }
374
 
    return -1;
375
 
  }
376
 
  
377
 
  if(debug){
378
 
    fprintf(stderr, "Binding to interface %s\n", interface);
379
 
  }
380
 
  
381
 
  memset(&to,0,sizeof(to));     /* Spurious warning */
382
 
  to.sin6_family = AF_INET6;
383
 
  ret = inet_pton(AF_INET6, ip, &to.sin6_addr);
384
 
  if (ret < 0 ){
385
 
    perror("inet_pton");
386
 
    return -1;
387
 
  }  
 
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
  }
388
745
  if(ret == 0){
389
 
    fprintf(stderr, "Bad address: %s\n", ip);
390
 
    return -1;
391
 
  }
392
 
  to.sin6_port = htons(port);   /* Spurious warning */
393
 
  
394
 
  to.sin6_scope_id = (uint32_t)if_index;
395
 
  
396
 
  if(debug){
397
 
    fprintf(stderr, "Connection to: %s\n", ip);
398
 
  }
399
 
  
400
 
  ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
401
 
  if (ret < 0){
402
 
    perror("connect");
403
 
    return -1;
404
 
  }
405
 
  
406
 
  ret = initgnutls (&es);
407
 
  if (ret != 0){
408
 
    retval = -1;
409
 
    return -1;
410
 
  }
411
 
  
412
 
  gnutls_transport_set_ptr (es.session,
413
 
                            (gnutls_transport_ptr_t) tcp_sd);
414
 
  
415
 
  if(debug){
416
 
    fprintf(stderr, "Establishing TLS session with %s\n", ip);
417
 
  }
418
 
  
419
 
  ret = gnutls_handshake (es.session);
420
 
  
421
 
  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){
422
893
    if(debug){
423
 
      fprintf(stderr, "\n*** Handshake failed ***\n");
424
 
      gnutls_perror (ret);
 
894
      fprintf_plus(stderr, "*** GnuTLS Handshake failed ***\n");
 
895
      gnutls_perror(ret);
425
896
    }
426
 
    retval = -1;
427
 
    goto exit;
 
897
    errno = EPROTO;
 
898
    goto mandos_end;
428
899
  }
429
900
  
430
 
  //Retrieve OpenPGP packet that contains the wanted password
 
901
  /* Read OpenPGP packet that contains the wanted password */
431
902
  
432
903
  if(debug){
433
 
    fprintf(stderr, "Retrieving pgp encrypted password from %s\n",
434
 
            ip);
 
904
    fprintf_plus(stderr, "Retrieving OpenPGP encrypted password from"
 
905
                 " %s\n", ip);
435
906
  }
436
 
 
 
907
  
437
908
  while(true){
438
 
    if (buffer_length + BUFFER_SIZE > buffer_capacity){
439
 
      buffer = realloc(buffer, buffer_capacity + BUFFER_SIZE);
440
 
      if (buffer == NULL){
441
 
        perror("realloc");
442
 
        goto exit;
443
 
      }
444
 
      buffer_capacity += BUFFER_SIZE;
445
 
    }
446
 
    
447
 
    ret = gnutls_record_recv
448
 
      (es.session, buffer+buffer_length, BUFFER_SIZE);
449
 
    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){
450
932
      break;
451
933
    }
452
 
    if (ret < 0){
453
 
      switch(ret){
 
934
    if(sret < 0){
 
935
      switch(sret){
454
936
      case GNUTLS_E_INTERRUPTED:
455
937
      case GNUTLS_E_AGAIN:
456
938
        break;
457
939
      case GNUTLS_E_REHANDSHAKE:
458
 
        ret = gnutls_handshake (es.session);
459
 
        if (ret < 0){
460
 
          fprintf(stderr, "\n*** Handshake failed ***\n");
461
 
          gnutls_perror (ret);
462
 
          retval = -1;
463
 
          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;
464
954
        }
465
955
        break;
466
956
      default:
467
 
        fprintf(stderr, "Unknown error while reading data from"
468
 
                " encrypted session with mandos server\n");
469
 
        retval = -1;
470
 
        gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
471
 
        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;
472
962
      }
473
963
    } else {
474
 
      buffer_length += (size_t) ret;
475
 
    }
476
 
  }
477
 
  
478
 
  if (buffer_length > 0){
479
 
    decrypted_buffer_size = pgp_packet_decrypt(buffer,
480
 
                                               buffer_length,
481
 
                                               &decrypted_buffer,
482
 
                                               CERT_ROOT);
483
 
    if (decrypted_buffer_size >= 0){
484
 
      while(written < decrypted_buffer_size){
485
 
        ret = (int)fwrite (decrypted_buffer + written, 1,
486
 
                           (size_t)decrypted_buffer_size - written,
487
 
                           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);
488
1001
        if(ret == 0 and ferror(stdout)){
 
1002
          int e = errno;
489
1003
          if(debug){
490
 
            fprintf(stderr, "Error writing encrypted data: %s\n",
491
 
                    strerror(errno));
 
1004
            fprintf_plus(stderr, "Error writing encrypted data: %s\n",
 
1005
                         strerror(errno));
492
1006
          }
493
 
          retval = -1;
494
 
          break;
 
1007
          errno = e;
 
1008
          goto mandos_end;
495
1009
        }
496
1010
        written += (size_t)ret;
497
1011
      }
498
 
      free(decrypted_buffer);
499
 
    } 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;
500
1036
      retval = -1;
501
1037
    }
502
1038
  }
503
 
 
504
 
  //shutdown procedure
505
 
 
506
 
  if(debug){
507
 
    fprintf(stderr, "Closing TLS session\n");
508
 
  }
509
 
 
510
 
  free(buffer);
511
 
  gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
512
 
 exit:
513
 
  close(tcp_sd);
514
 
  gnutls_deinit (es.session);
515
 
  gnutls_certificate_free_credentials (es.cred);
516
 
  gnutls_global_deinit ();
517
1039
  return retval;
518
1040
}
519
1041
 
520
 
static AvahiSimplePoll *simple_poll = NULL;
521
 
static AvahiServer *server = NULL;
522
 
 
523
 
static void resolve_callback(
524
 
    AvahiSServiceResolver *r,
525
 
    AvahiIfIndex interface,
526
 
    AVAHI_GCC_UNUSED AvahiProtocol protocol,
527
 
    AvahiResolverEvent event,
528
 
    const char *name,
529
 
    const char *type,
530
 
    const char *domain,
531
 
    const char *host_name,
532
 
    const AvahiAddress *address,
533
 
    uint16_t port,
534
 
    AVAHI_GCC_UNUSED AvahiStringList *txt,
535
 
    AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
536
 
    AVAHI_GCC_UNUSED void* userdata) {
537
 
    
538
 
  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
  }
539
1060
  
540
1061
  /* Called whenever a service has been resolved successfully or
541
1062
     timed out */
542
1063
  
543
 
  switch (event) {
 
1064
  if(quit_now){
 
1065
    return;
 
1066
  }
 
1067
  
 
1068
  switch(event){
544
1069
  default:
545
1070
  case AVAHI_RESOLVER_FAILURE:
546
 
    fprintf(stderr, "(Resolver) Failed to resolve service '%s' of"
547
 
            " type '%s' in domain '%s': %s\n", name, type, domain,
548
 
            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)));
549
1076
    break;
550
1077
    
551
1078
  case AVAHI_RESOLVER_FOUND:
553
1080
      char ip[AVAHI_ADDRESS_STR_MAX];
554
1081
      avahi_address_snprint(ip, sizeof(ip), address);
555
1082
      if(debug){
556
 
        fprintf(stderr, "Mandos server \"%s\" found on %s (%s) on"
557
 
                " 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);
558
1086
      }
559
 
      int ret = start_mandos_communication(ip, port,
560
 
                                           (unsigned int) interface);
561
 
      if (ret == 0){
562
 
        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
        }
563
1100
      }
564
1101
    }
565
1102
  }
566
1103
  avahi_s_service_resolver_free(r);
567
1104
}
568
1105
 
569
 
static void browse_callback(
570
 
    AvahiSServiceBrowser *b,
571
 
    AvahiIfIndex interface,
572
 
    AvahiProtocol protocol,
573
 
    AvahiBrowserEvent event,
574
 
    const char *name,
575
 
    const char *type,
576
 
    const char *domain,
577
 
    AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
578
 
    void* userdata) {
579
 
    
580
 
    AvahiServer *s = userdata;
581
 
    assert(b);                  /* Spurious warning */
582
 
    
583
 
    /* Called whenever a new services becomes available on the LAN or
584
 
       is removed from the LAN */
585
 
    
586
 
    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:
587
2048
    default:
588
 
    case AVAHI_BROWSER_FAILURE:
589
 
      
590
 
      fprintf(stderr, "(Browser) %s\n",
591
 
              avahi_strerror(avahi_server_errno(server)));
592
 
      avahi_simple_poll_quit(simple_poll);
593
 
      return;
594
 
      
595
 
    case AVAHI_BROWSER_NEW:
596
 
      /* We ignore the returned resolver object. In the callback
597
 
         function we free it. If the server is terminated before
598
 
         the callback function is called the server will free
599
 
         the resolver for us. */
600
 
      
601
 
      if (!(avahi_s_service_resolver_new(s, interface, protocol, name,
602
 
                                         type, domain,
603
 
                                         AVAHI_PROTO_INET6, 0,
604
 
                                         resolve_callback, s)))
605
 
        fprintf(stderr, "Failed to resolve service '%s': %s\n", name,
606
 
                avahi_strerror(avahi_server_errno(s)));
607
 
      break;
608
 
      
609
 
    case AVAHI_BROWSER_REMOVE:
610
 
      break;
611
 
      
612
 
    case AVAHI_BROWSER_ALL_FOR_NOW:
613
 
    case AVAHI_BROWSER_CACHE_EXHAUSTED:
614
 
      break;
615
 
    }
616
 
}
617
 
 
618
 
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
  {
619
2427
    AvahiServerConfig config;
620
 
    AvahiSServiceBrowser *sb = NULL;
621
 
    int error;
622
 
    int ret;
623
 
    int returncode = EXIT_SUCCESS;
624
 
    const char *interface = "eth0";
625
 
    
626
 
    while (true){
627
 
      static struct option long_options[] = {
628
 
        {"debug", no_argument, (int *)&debug, 1},
629
 
        {"interface", required_argument, 0, 'i'},
630
 
        {0, 0, 0, 0} };
631
 
      
632
 
      int option_index = 0;
633
 
      ret = getopt_long (argc, argv, "i:", long_options,
634
 
                         &option_index);
635
 
      
636
 
      if (ret == -1){
637
 
        break;
638
 
      }
639
 
      
640
 
      switch(ret){
641
 
      case 0:
642
 
        break;
643
 
      case 'i':
644
 
        interface = optarg;
645
 
        break;
646
 
      default:
647
 
        exit(EXIT_FAILURE);
648
 
      }
649
 
    }
650
 
    
651
 
    if (not debug){
652
 
      avahi_set_log_function(empty_log);
653
 
    }
654
 
    
655
 
    /* Initialize the psuedo-RNG */
656
 
    srand((unsigned int) time(NULL));
657
 
 
658
 
    /* Allocate main loop object */
659
 
    if (!(simple_poll = avahi_simple_poll_new())) {
660
 
        fprintf(stderr, "Failed to create simple poll object.\n");
661
 
        
662
 
        goto exit;
663
 
    }
664
 
 
665
 
    /* Do not publish any local records */
 
2428
    /* Do not publish any local Zeroconf records */
666
2429
    avahi_server_config_init(&config);
667
2430
    config.publish_hinfo = 0;
668
2431
    config.publish_addresses = 0;
669
2432
    config.publish_workstation = 0;
670
2433
    config.publish_domain = 0;
671
 
 
 
2434
    
672
2435
    /* Allocate a new server */
673
 
    server = avahi_server_new(avahi_simple_poll_get(simple_poll),
674
 
                              &config, NULL, NULL, &error);
675
 
 
676
 
    /* 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 */
677
2440
    avahi_server_config_free(&config);
678
 
 
679
 
    /* Check if creating the server object succeeded */
680
 
    if (!server) {
681
 
        fprintf(stderr, "Failed to create server: %s\n",
682
 
                avahi_strerror(error));
683
 
        returncode = EXIT_FAILURE;
684
 
        goto exit;
685
 
    }
686
 
    
687
 
    /* Create the service browser */
688
 
    sb = avahi_s_service_browser_new(server,
689
 
                                     (AvahiIfIndex)
690
 
                                     if_nametoindex(interface),
691
 
                                     AVAHI_PROTO_INET6,
692
 
                                     "_mandos._tcp", NULL, 0,
693
 
                                     browse_callback, server);
694
 
    if (!sb) {
695
 
        fprintf(stderr, "Failed to create service browser: %s\n",
696
 
                avahi_strerror(avahi_server_errno(server)));
697
 
        returncode = EXIT_FAILURE;
698
 
        goto exit;
699
 
    }
700
 
    
701
 
    /* Run the main loop */
702
 
 
703
 
    if (debug){
704
 
      fprintf(stderr, "Starting avahi loop search\n");
705
 
    }
706
 
    
707
 
    avahi_simple_poll_loop(simple_poll);
708
 
    
709
 
 exit:
710
 
 
711
 
    if (debug){
712
 
      fprintf(stderr, "%s exiting\n", argv[0]);
713
 
    }
714
 
    
715
 
    /* Cleanup things */
716
 
    if (sb)
717
 
        avahi_s_service_browser_free(sb);
718
 
    
719
 
    if (server)
720
 
        avahi_server_free(server);
721
 
 
722
 
    if (simple_poll)
723
 
        avahi_simple_poll_free(simple_poll);
724
 
 
725
 
    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;
726
2617
}