/mandos/release

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

« back to all changes in this revision

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

Change "fukt.bsnet.se" to "recompile.se" throughout.

* README: - '' -
* debian/control: - '' -
* debian/copyright: - '' -
* debian/mandos-client.README.Debian: - '' - and some rewriting.
* debian/mandos.README.Debian: - '' -
* debian/watch: Change "fukt.bsnet.se" to "recompile.se".
* init.d-mandos: - '' -
* intro.xml: - '' -
* mandos: - '' -
* mandos-clients.conf.xml: - '' -
* mandos-ctl: - '' -
* mandos-ctl.xml: - '' -
* mandos-keygen: - '' -
* mandos-keygen.xml: - '' -
* mandos-monitor: - '' -
* mandos-monitor.xml: - '' -
* mandos.conf.xml: - '' -
* mandos.lsm: - '' -
* mandos.xml: - '' -
* plugin-runner.c: - '' -
* plugin-runner.xml: - '' -
* plugins.d/askpass-fifo.c: - '' -
* plugins.d/askpass-fifo.xml: - '' -
* plugins.d/mandos-client.c: - '' -
* plugins.d/mandos-client.xml: - '' -
* plugins.d/password-prompt.c: - '' -
* plugins.d/password-prompt.xml: - '' -
* plugins.d/plymouth.c: - '' -
* plugins.d/plymouth.xml: - '' -
* plugins.d/splashy.c: - '' -
* plugins.d/splashy.xml: - '' -
* plugins.d/usplash.c: - '' -
* plugins.d/usplash.xml: - '' -

Show diffs side-by-side

added added

removed removed

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