/mandos/trunk

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

« back to all changes in this revision

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

  • Committer: Björn Påhlsson
  • Date: 2011-11-14 18:03:35 UTC
  • mto: (505.3.9 client-network-hooks)
  • mto: This revision was merged to the branch mainline in revision 525.
  • Revision ID: belorn@fukt.bsnet.se-20111114180335-3j4x5mjr7lix2jyj
New convinence error printer: fprintf_plus

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