/mandos/trunk

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

« back to all changes in this revision

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

  • Committer: Teddy Hogeborn
  • Date: 2009-08-27 23:12:09 UTC
  • Revision ID: teddy@fukt.bsnet.se-20090827231209-ry9loqws0gr7ze4g
* plugins.d/mandos-client.c (quit_now): Move up declaration before
                                        first use.
  (resolve_callback, ): Check "quit_now" flag.
  (main): Renamed "interface_taken_up" to "take_down_interface" for
          clarity.  Check existence of interface before taking it up.
          Set "take_down_interface" and "tempdir_created" flags before
          any action is taken, to make sure cleanup is never missed.
          Check "quit_now" flag throughout.

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,2009 Teddy Hogeborn
 
13
 * Copyright © 2008,2009 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
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
 
#include <sys/ioctl.h>          // ioctl, ifreq, SIOCGIFFLAGS, IFF_UP, SIOCSIFFLAGS
41
 
#include <net/if.h>             // ioctl, ifreq, SIOCGIFFLAGS, IFF_UP, SIOCSIFFLAGS
42
 
 
 
38
#endif
 
39
 
 
40
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY(), asprintf() */
 
41
 
 
42
#include <stdio.h>              /* fprintf(), stderr, fwrite(),
 
43
                                   stdout, ferror(), remove() */
 
44
#include <stdint.h>             /* uint16_t, uint32_t */
 
45
#include <stddef.h>             /* NULL, size_t, ssize_t */
 
46
#include <stdlib.h>             /* free(), EXIT_SUCCESS, EXIT_FAILURE,
 
47
                                   srand(), strtof() */
 
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
#include <time.h>               /* nanosleep(), time() */
 
67
#include <net/if.h>             /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
 
68
                                   SIOCSIFFLAGS, if_indextoname(),
 
69
                                   if_nametoindex(), IF_NAMESIZE */
 
70
#include <netinet/in.h>         /* IN6_IS_ADDR_LINKLOCAL,
 
71
                                   INET_ADDRSTRLEN, INET6_ADDRSTRLEN
 
72
                                */
 
73
#include <unistd.h>             /* close(), SEEK_SET, off_t, write(),
 
74
                                   getuid(), getgid(), setuid(),
 
75
                                   setgid() */
 
76
#include <arpa/inet.h>          /* inet_pton(), htons */
 
77
#include <iso646.h>             /* not, or, and */
 
78
#include <argp.h>               /* struct argp_option, error_t, struct
 
79
                                   argp_state, struct argp,
 
80
                                   argp_parse(), ARGP_KEY_ARG,
 
81
                                   ARGP_KEY_END, ARGP_ERR_UNKNOWN */
 
82
#include <signal.h>             /* sigemptyset(), sigaddset(),
 
83
                                   sigaction(), SIGTERM, sigaction,
 
84
                                   sig_atomic_t */
 
85
 
 
86
#ifdef __linux__
 
87
#include <sys/klog.h>           /* klogctl() */
 
88
#endif  /* __linux__ */
 
89
 
 
90
/* Avahi */
 
91
/* All Avahi types, constants and functions
 
92
 Avahi*, avahi_*,
 
93
 AVAHI_* */
43
94
#include <avahi-core/core.h>
44
95
#include <avahi-core/lookup.h>
45
96
#include <avahi-core/log.h>
47
98
#include <avahi-common/malloc.h>
48
99
#include <avahi-common/error.h>
49
100
 
50
 
//mandos client part
51
 
#include <sys/types.h>          /* socket(), inet_pton() */
52
 
#include <sys/socket.h>         /* socket(), struct sockaddr_in6,
53
 
                                   struct in6_addr, inet_pton() */
54
 
#include <gnutls/gnutls.h>      /* All GnuTLS stuff */
55
 
#include <gnutls/openpgp.h>     /* GnuTLS with openpgp stuff */
56
 
 
57
 
#include <unistd.h>             /* close() */
58
 
#include <netinet/in.h>
59
 
#include <stdbool.h>            /* true */
60
 
#include <string.h>             /* memset */
61
 
#include <arpa/inet.h>          /* inet_pton() */
62
 
#include <iso646.h>             /* not */
63
 
 
64
 
// gpgme
65
 
#include <errno.h>              /* perror() */
66
 
#include <gpgme.h>
67
 
 
68
 
// getopt long
69
 
#include <getopt.h>
 
101
/* GnuTLS */
 
102
#include <gnutls/gnutls.h>      /* All GnuTLS types, constants and
 
103
                                   functions:
 
104
                                   gnutls_*
 
105
                                   init_gnutls_session(),
 
106
                                   GNUTLS_* */
 
107
#include <gnutls/openpgp.h>
 
108
                          /* gnutls_certificate_set_openpgp_key_file(),
 
109
                                   GNUTLS_OPENPGP_FMT_BASE64 */
 
110
 
 
111
/* GPGME */
 
112
#include <gpgme.h>              /* All GPGME types, constants and
 
113
                                   functions:
 
114
                                   gpgme_*
 
115
                                   GPGME_PROTOCOL_OpenPGP,
 
116
                                   GPG_ERR_NO_* */
70
117
 
71
118
#define BUFFER_SIZE 256
72
 
#define DH_BITS 1024
73
119
 
74
 
static const char *certdir = "/conf/conf.d/mandos";
75
 
static const char *certfile = "openpgp-client.txt";
76
 
static const char *certkey = "openpgp-client-key.txt";
 
120
#define PATHDIR "/conf/conf.d/mandos"
 
121
#define SECKEY "seckey.txt"
 
122
#define PUBKEY "pubkey.txt"
77
123
 
78
124
bool debug = false;
 
125
static const char mandos_protocol_version[] = "1";
 
126
const char *argp_program_version = "mandos-client " VERSION;
 
127
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
79
128
 
 
129
/* Used for passing in values through the Avahi callback functions */
80
130
typedef struct {
81
131
  AvahiSimplePoll *simple_poll;
82
132
  AvahiServer *server;
83
133
  gnutls_certificate_credentials_t cred;
84
134
  unsigned int dh_bits;
 
135
  gnutls_dh_params_t dh_params;
85
136
  const char *priority;
 
137
  gpgme_ctx_t ctx;
86
138
} mandos_context;
87
139
 
88
 
static ssize_t pgp_packet_decrypt (char *packet, size_t packet_size,
89
 
                                   char **new_packet,
90
 
                                   const char *homedir){
91
 
  gpgme_data_t dh_crypto, dh_plain;
92
 
  gpgme_ctx_t ctx;
 
140
/* global context so signal handler can reach it*/
 
141
mandos_context mc = { .simple_poll = NULL, .server = NULL,
 
142
                      .dh_bits = 1024, .priority = "SECURE256"
 
143
                      ":!CTYPE-X.509:+CTYPE-OPENPGP" };
 
144
 
 
145
/*
 
146
 * Make additional room in "buffer" for at least BUFFER_SIZE more
 
147
 * bytes. "buffer_capacity" is how much is currently allocated,
 
148
 * "buffer_length" is how much is already used.
 
149
 */
 
150
size_t incbuffer(char **buffer, size_t buffer_length,
 
151
                  size_t buffer_capacity){
 
152
  if(buffer_length + BUFFER_SIZE > buffer_capacity){
 
153
    *buffer = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
 
154
    if(buffer == NULL){
 
155
      return 0;
 
156
    }
 
157
    buffer_capacity += BUFFER_SIZE;
 
158
  }
 
159
  return buffer_capacity;
 
160
}
 
161
 
 
162
/* 
 
163
 * Initialize GPGME.
 
164
 */
 
165
static bool init_gpgme(const char *seckey,
 
166
                       const char *pubkey, const char *tempdir){
 
167
  int ret;
93
168
  gpgme_error_t rc;
94
 
  ssize_t ret;
95
 
  ssize_t new_packet_capacity = 0;
96
 
  ssize_t new_packet_length = 0;
97
169
  gpgme_engine_info_t engine_info;
98
 
 
99
 
  if (debug){
100
 
    fprintf(stderr, "Trying to decrypt OpenPGP packet\n");
 
170
  
 
171
  
 
172
  /*
 
173
   * Helper function to insert pub and seckey to the engine keyring.
 
174
   */
 
175
  bool import_key(const char *filename){
 
176
    int fd;
 
177
    gpgme_data_t pgp_data;
 
178
    
 
179
    fd = (int)TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
 
180
    if(fd == -1){
 
181
      perror("open");
 
182
      return false;
 
183
    }
 
184
    
 
185
    rc = gpgme_data_new_from_fd(&pgp_data, fd);
 
186
    if(rc != GPG_ERR_NO_ERROR){
 
187
      fprintf(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
 
188
              gpgme_strsource(rc), gpgme_strerror(rc));
 
189
      return false;
 
190
    }
 
191
    
 
192
    rc = gpgme_op_import(mc.ctx, pgp_data);
 
193
    if(rc != GPG_ERR_NO_ERROR){
 
194
      fprintf(stderr, "bad gpgme_op_import: %s: %s\n",
 
195
              gpgme_strsource(rc), gpgme_strerror(rc));
 
196
      return false;
 
197
    }
 
198
    
 
199
    ret = (int)TEMP_FAILURE_RETRY(close(fd));
 
200
    if(ret == -1){
 
201
      perror("close");
 
202
    }
 
203
    gpgme_data_release(pgp_data);
 
204
    return true;
 
205
  }
 
206
  
 
207
  if(debug){
 
208
    fprintf(stderr, "Initializing GPGME\n");
101
209
  }
102
210
  
103
211
  /* Init GPGME */
104
212
  gpgme_check_version(NULL);
105
213
  rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
106
 
  if (rc != GPG_ERR_NO_ERROR){
 
214
  if(rc != GPG_ERR_NO_ERROR){
107
215
    fprintf(stderr, "bad gpgme_engine_check_version: %s: %s\n",
108
216
            gpgme_strsource(rc), gpgme_strerror(rc));
109
 
    return -1;
 
217
    return false;
110
218
  }
111
219
  
112
 
  /* Set GPGME home directory */
113
 
  rc = gpgme_get_engine_info (&engine_info);
114
 
  if (rc != GPG_ERR_NO_ERROR){
 
220
    /* Set GPGME home directory for the OpenPGP engine only */
 
221
  rc = gpgme_get_engine_info(&engine_info);
 
222
  if(rc != GPG_ERR_NO_ERROR){
115
223
    fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
116
224
            gpgme_strsource(rc), gpgme_strerror(rc));
117
 
    return -1;
 
225
    return false;
118
226
  }
119
227
  while(engine_info != NULL){
120
228
    if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
121
229
      gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
122
 
                            engine_info->file_name, homedir);
 
230
                            engine_info->file_name, tempdir);
123
231
      break;
124
232
    }
125
233
    engine_info = engine_info->next;
126
234
  }
127
235
  if(engine_info == NULL){
128
 
    fprintf(stderr, "Could not set home dir to %s\n", homedir);
129
 
    return -1;
130
 
  }
131
 
  
132
 
  /* Create new GPGME data buffer from packet buffer */
133
 
  rc = gpgme_data_new_from_mem(&dh_crypto, packet, packet_size, 0);
134
 
  if (rc != GPG_ERR_NO_ERROR){
 
236
    fprintf(stderr, "Could not set GPGME home dir to %s\n", tempdir);
 
237
    return false;
 
238
  }
 
239
  
 
240
  /* Create new GPGME "context" */
 
241
  rc = gpgme_new(&(mc.ctx));
 
242
  if(rc != GPG_ERR_NO_ERROR){
 
243
    fprintf(stderr, "bad gpgme_new: %s: %s\n",
 
244
            gpgme_strsource(rc), gpgme_strerror(rc));
 
245
    return false;
 
246
  }
 
247
  
 
248
  if(not import_key(pubkey) or not import_key(seckey)){
 
249
    return false;
 
250
  }
 
251
  
 
252
  return true; 
 
253
}
 
254
 
 
255
/* 
 
256
 * Decrypt OpenPGP data.
 
257
 * Returns -1 on error
 
258
 */
 
259
static ssize_t pgp_packet_decrypt(const char *cryptotext,
 
260
                                  size_t crypto_size,
 
261
                                  char **plaintext){
 
262
  gpgme_data_t dh_crypto, dh_plain;
 
263
  gpgme_error_t rc;
 
264
  ssize_t ret;
 
265
  size_t plaintext_capacity = 0;
 
266
  ssize_t plaintext_length = 0;
 
267
  
 
268
  if(debug){
 
269
    fprintf(stderr, "Trying to decrypt OpenPGP data\n");
 
270
  }
 
271
  
 
272
  /* Create new GPGME data buffer from memory cryptotext */
 
273
  rc = gpgme_data_new_from_mem(&dh_crypto, cryptotext, crypto_size,
 
274
                               0);
 
275
  if(rc != GPG_ERR_NO_ERROR){
135
276
    fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
136
277
            gpgme_strsource(rc), gpgme_strerror(rc));
137
278
    return -1;
139
280
  
140
281
  /* Create new empty GPGME data buffer for the plaintext */
141
282
  rc = gpgme_data_new(&dh_plain);
142
 
  if (rc != GPG_ERR_NO_ERROR){
 
283
  if(rc != GPG_ERR_NO_ERROR){
143
284
    fprintf(stderr, "bad gpgme_data_new: %s: %s\n",
144
285
            gpgme_strsource(rc), gpgme_strerror(rc));
145
 
    return -1;
146
 
  }
147
 
  
148
 
  /* Create new GPGME "context" */
149
 
  rc = gpgme_new(&ctx);
150
 
  if (rc != GPG_ERR_NO_ERROR){
151
 
    fprintf(stderr, "bad gpgme_new: %s: %s\n",
152
 
            gpgme_strsource(rc), gpgme_strerror(rc));
153
 
    return -1;
154
 
  }
155
 
  
156
 
  /* Decrypt data from the FILE pointer to the plaintext data
157
 
     buffer */
158
 
  rc = gpgme_op_decrypt(ctx, dh_crypto, dh_plain);
159
 
  if (rc != GPG_ERR_NO_ERROR){
 
286
    gpgme_data_release(dh_crypto);
 
287
    return -1;
 
288
  }
 
289
  
 
290
  /* Decrypt data from the cryptotext data buffer to the plaintext
 
291
     data buffer */
 
292
  rc = gpgme_op_decrypt(mc.ctx, dh_crypto, dh_plain);
 
293
  if(rc != GPG_ERR_NO_ERROR){
160
294
    fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
161
295
            gpgme_strsource(rc), gpgme_strerror(rc));
162
 
    return -1;
163
 
  }
164
 
 
165
 
  if(debug){
166
 
    fprintf(stderr, "Decryption of OpenPGP packet succeeded\n");
167
 
  }
168
 
 
169
 
  if (debug){
170
 
    gpgme_decrypt_result_t result;
171
 
    result = gpgme_op_decrypt_result(ctx);
172
 
    if (result == NULL){
173
 
      fprintf(stderr, "gpgme_op_decrypt_result failed\n");
174
 
    } else {
175
 
      fprintf(stderr, "Unsupported algorithm: %s\n",
176
 
              result->unsupported_algorithm);
177
 
      fprintf(stderr, "Wrong key usage: %d\n",
178
 
              result->wrong_key_usage);
179
 
      if(result->file_name != NULL){
180
 
        fprintf(stderr, "File name: %s\n", result->file_name);
181
 
      }
182
 
      gpgme_recipient_t recipient;
183
 
      recipient = result->recipients;
184
 
      if(recipient){
 
296
    plaintext_length = -1;
 
297
    if(debug){
 
298
      gpgme_decrypt_result_t result;
 
299
      result = gpgme_op_decrypt_result(mc.ctx);
 
300
      if(result == NULL){
 
301
        fprintf(stderr, "gpgme_op_decrypt_result failed\n");
 
302
      } else {
 
303
        fprintf(stderr, "Unsupported algorithm: %s\n",
 
304
                result->unsupported_algorithm);
 
305
        fprintf(stderr, "Wrong key usage: %u\n",
 
306
                result->wrong_key_usage);
 
307
        if(result->file_name != NULL){
 
308
          fprintf(stderr, "File name: %s\n", result->file_name);
 
309
        }
 
310
        gpgme_recipient_t recipient;
 
311
        recipient = result->recipients;
185
312
        while(recipient != NULL){
186
313
          fprintf(stderr, "Public key algorithm: %s\n",
187
314
                  gpgme_pubkey_algo_name(recipient->pubkey_algo));
193
320
        }
194
321
      }
195
322
    }
 
323
    goto decrypt_end;
196
324
  }
197
325
  
198
 
  /* Delete the GPGME FILE pointer cryptotext data buffer */
199
 
  gpgme_data_release(dh_crypto);
 
326
  if(debug){
 
327
    fprintf(stderr, "Decryption of OpenPGP data succeeded\n");
 
328
  }
200
329
  
201
330
  /* Seek back to the beginning of the GPGME plaintext data buffer */
202
 
  if (gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET) == -1){
203
 
    perror("pgpme_data_seek");
 
331
  if(gpgme_data_seek(dh_plain, (off_t)0, SEEK_SET) == -1){
 
332
    perror("gpgme_data_seek");
 
333
    plaintext_length = -1;
 
334
    goto decrypt_end;
204
335
  }
205
336
  
206
 
  *new_packet = 0;
 
337
  *plaintext = NULL;
207
338
  while(true){
208
 
    if (new_packet_length + BUFFER_SIZE > new_packet_capacity){
209
 
      *new_packet = realloc(*new_packet,
210
 
                            (unsigned int)new_packet_capacity
211
 
                            + BUFFER_SIZE);
212
 
      if (*new_packet == NULL){
213
 
        perror("realloc");
214
 
        return -1;
215
 
      }
216
 
      new_packet_capacity += BUFFER_SIZE;
 
339
    plaintext_capacity = incbuffer(plaintext,
 
340
                                      (size_t)plaintext_length,
 
341
                                      plaintext_capacity);
 
342
    if(plaintext_capacity == 0){
 
343
        perror("incbuffer");
 
344
        plaintext_length = -1;
 
345
        goto decrypt_end;
217
346
    }
218
347
    
219
 
    ret = gpgme_data_read(dh_plain, *new_packet + new_packet_length,
 
348
    ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
220
349
                          BUFFER_SIZE);
221
350
    /* Print the data, if any */
222
 
    if (ret == 0){
 
351
    if(ret == 0){
 
352
      /* EOF */
223
353
      break;
224
354
    }
225
355
    if(ret < 0){
226
356
      perror("gpgme_data_read");
227
 
      return -1;
228
 
    }
229
 
    new_packet_length += ret;
230
 
  }
231
 
 
232
 
  /* FIXME: check characters before printing to screen so to not print
233
 
     terminal control characters */
234
 
  /*   if(debug){ */
235
 
  /*     fprintf(stderr, "decrypted password is: "); */
236
 
  /*     fwrite(*new_packet, 1, new_packet_length, stderr); */
237
 
  /*     fprintf(stderr, "\n"); */
238
 
  /*   } */
 
357
      plaintext_length = -1;
 
358
      goto decrypt_end;
 
359
    }
 
360
    plaintext_length += ret;
 
361
  }
 
362
  
 
363
  if(debug){
 
364
    fprintf(stderr, "Decrypted password is: ");
 
365
    for(ssize_t i = 0; i < plaintext_length; i++){
 
366
      fprintf(stderr, "%02hhX ", (*plaintext)[i]);
 
367
    }
 
368
    fprintf(stderr, "\n");
 
369
  }
 
370
  
 
371
 decrypt_end:
 
372
  
 
373
  /* Delete the GPGME cryptotext data buffer */
 
374
  gpgme_data_release(dh_crypto);
239
375
  
240
376
  /* Delete the GPGME plaintext data buffer */
241
377
  gpgme_data_release(dh_plain);
242
 
  return new_packet_length;
 
378
  return plaintext_length;
243
379
}
244
380
 
245
 
static const char * safer_gnutls_strerror (int value) {
246
 
  const char *ret = gnutls_strerror (value);
247
 
  if (ret == NULL)
 
381
static const char * safer_gnutls_strerror(int value){
 
382
  const char *ret = gnutls_strerror(value); /* Spurious warning from
 
383
                                               -Wunreachable-code */
 
384
  if(ret == NULL)
248
385
    ret = "(unknown)";
249
386
  return ret;
250
387
}
251
388
 
 
389
/* GnuTLS log function callback */
252
390
static void debuggnutls(__attribute__((unused)) int level,
253
391
                        const char* string){
254
 
  fprintf(stderr, "%s", string);
 
392
  fprintf(stderr, "GnuTLS: %s", string);
255
393
}
256
394
 
257
 
static int initgnutls(mandos_context *mc){
258
 
  const char *err;
 
395
static int init_gnutls_global(const char *pubkeyfilename,
 
396
                              const char *seckeyfilename){
259
397
  int ret;
260
398
  
261
399
  if(debug){
262
400
    fprintf(stderr, "Initializing GnuTLS\n");
263
401
  }
264
 
 
265
 
  if ((ret = gnutls_global_init ())
266
 
      != GNUTLS_E_SUCCESS) {
267
 
    fprintf (stderr, "global_init: %s\n", safer_gnutls_strerror(ret));
 
402
  
 
403
  ret = gnutls_global_init();
 
404
  if(ret != GNUTLS_E_SUCCESS){
 
405
    fprintf(stderr, "GnuTLS global_init: %s\n",
 
406
            safer_gnutls_strerror(ret));
268
407
    return -1;
269
408
  }
270
 
 
271
 
  if (debug){
 
409
  
 
410
  if(debug){
 
411
    /* "Use a log level over 10 to enable all debugging options."
 
412
     * - GnuTLS manual
 
413
     */
272
414
    gnutls_global_set_log_level(11);
273
415
    gnutls_global_set_log_function(debuggnutls);
274
416
  }
275
417
  
276
 
  /* openpgp credentials */
277
 
  if ((ret = gnutls_certificate_allocate_credentials (&es->cred))
278
 
      != GNUTLS_E_SUCCESS) {
279
 
    fprintf (stderr, "memory error: %s\n",
280
 
             safer_gnutls_strerror(ret));
 
418
  /* OpenPGP credentials */
 
419
  gnutls_certificate_allocate_credentials(&mc.cred);
 
420
  if(ret != GNUTLS_E_SUCCESS){
 
421
    fprintf(stderr, "GnuTLS memory error: %s\n", /* Spurious warning
 
422
                                                    from
 
423
                                                    -Wunreachable-code
 
424
                                                 */
 
425
            safer_gnutls_strerror(ret));
 
426
    gnutls_global_deinit();
281
427
    return -1;
282
428
  }
283
429
  
284
430
  if(debug){
285
 
    fprintf(stderr, "Attempting to use OpenPGP certificate %s"
286
 
            " and keyfile %s as GnuTLS credentials\n", certfile,
287
 
            certkey);
 
431
    fprintf(stderr, "Attempting to use OpenPGP public key %s and"
 
432
            " secret key %s as GnuTLS credentials\n", pubkeyfilename,
 
433
            seckeyfilename);
288
434
  }
289
435
  
290
436
  ret = gnutls_certificate_set_openpgp_key_file
291
 
    (es->cred, certfile, certkey, GNUTLS_OPENPGP_FMT_BASE64);
292
 
  if (ret != GNUTLS_E_SUCCESS) {
293
 
    fprintf
294
 
      (stderr, "Error[%d] while reading the OpenPGP key pair ('%s',"
295
 
       " '%s')\n",
296
 
       ret, certfile, certkey);
297
 
    fprintf(stdout, "The Error is: %s\n",
298
 
            safer_gnutls_strerror(ret));
299
 
    return -1;
300
 
  }
301
 
  
302
 
  //GnuTLS server initialization
303
 
  if ((ret = gnutls_dh_params_init (&es->dh_params))
304
 
      != GNUTLS_E_SUCCESS) {
305
 
    fprintf (stderr, "Error in dh parameter initialization: %s\n",
306
 
             safer_gnutls_strerror(ret));
307
 
    return -1;
308
 
  }
309
 
  
310
 
  if ((ret = gnutls_dh_params_generate2 (es->dh_params, DH_BITS))
311
 
      != GNUTLS_E_SUCCESS) {
312
 
    fprintf (stderr, "Error in prime generation: %s\n",
313
 
             safer_gnutls_strerror(ret));
314
 
    return -1;
315
 
  }
316
 
  
317
 
  gnutls_certificate_set_dh_params (es->cred, es->dh_params);
318
 
  
319
 
  // GnuTLS session creation
320
 
  if ((ret = gnutls_init (&es->session, GNUTLS_SERVER))
321
 
      != GNUTLS_E_SUCCESS){
 
437
    (mc.cred, pubkeyfilename, seckeyfilename,
 
438
     GNUTLS_OPENPGP_FMT_BASE64);
 
439
  if(ret != GNUTLS_E_SUCCESS){
 
440
    fprintf(stderr,
 
441
            "Error[%d] while reading the OpenPGP key pair ('%s',"
 
442
            " '%s')\n", ret, pubkeyfilename, seckeyfilename);
 
443
    fprintf(stderr, "The GnuTLS error is: %s\n",
 
444
            safer_gnutls_strerror(ret));
 
445
    goto globalfail;
 
446
  }
 
447
  
 
448
  /* GnuTLS server initialization */
 
449
  ret = gnutls_dh_params_init(&mc.dh_params);
 
450
  if(ret != GNUTLS_E_SUCCESS){
 
451
    fprintf(stderr, "Error in GnuTLS DH parameter initialization:"
 
452
            " %s\n", safer_gnutls_strerror(ret));
 
453
    goto globalfail;
 
454
  }
 
455
  ret = gnutls_dh_params_generate2(mc.dh_params, mc.dh_bits);
 
456
  if(ret != GNUTLS_E_SUCCESS){
 
457
    fprintf(stderr, "Error in GnuTLS prime generation: %s\n",
 
458
            safer_gnutls_strerror(ret));
 
459
    goto globalfail;
 
460
  }
 
461
  
 
462
  gnutls_certificate_set_dh_params(mc.cred, mc.dh_params);
 
463
  
 
464
  return 0;
 
465
  
 
466
 globalfail:
 
467
  
 
468
  gnutls_certificate_free_credentials(mc.cred);
 
469
  gnutls_global_deinit();
 
470
  gnutls_dh_params_deinit(mc.dh_params);
 
471
  return -1;
 
472
}
 
473
 
 
474
static int init_gnutls_session(gnutls_session_t *session){
 
475
  int ret;
 
476
  /* GnuTLS session creation */
 
477
  ret = gnutls_init(session, GNUTLS_SERVER);
 
478
  if(ret != GNUTLS_E_SUCCESS){
322
479
    fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
323
480
            safer_gnutls_strerror(ret));
324
481
  }
325
482
  
326
 
  if ((ret = gnutls_priority_set_direct (es->session, mc->priority, &err))
327
 
      != GNUTLS_E_SUCCESS) {
328
 
    fprintf(stderr, "Syntax error at: %s\n", err);
329
 
    fprintf(stderr, "GnuTLS error: %s\n",
330
 
            safer_gnutls_strerror(ret));
331
 
    return -1;
 
483
  {
 
484
    const char *err;
 
485
    ret = gnutls_priority_set_direct(*session, mc.priority, &err);
 
486
    if(ret != GNUTLS_E_SUCCESS){
 
487
      fprintf(stderr, "Syntax error at: %s\n", err);
 
488
      fprintf(stderr, "GnuTLS error: %s\n",
 
489
              safer_gnutls_strerror(ret));
 
490
      gnutls_deinit(*session);
 
491
      return -1;
 
492
    }
332
493
  }
333
494
  
334
 
  if ((ret = gnutls_credentials_set
335
 
       (es->session, GNUTLS_CRD_CERTIFICATE, es->cred))
336
 
      != GNUTLS_E_SUCCESS) {
337
 
    fprintf(stderr, "Error setting a credentials set: %s\n",
 
495
  ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
 
496
                               mc.cred);
 
497
  if(ret != GNUTLS_E_SUCCESS){
 
498
    fprintf(stderr, "Error setting GnuTLS credentials: %s\n",
338
499
            safer_gnutls_strerror(ret));
 
500
    gnutls_deinit(*session);
339
501
    return -1;
340
502
  }
341
503
  
342
504
  /* ignore client certificate if any. */
343
 
  gnutls_certificate_server_set_request (es->session,
344
 
                                         GNUTLS_CERT_IGNORE);
 
505
  gnutls_certificate_server_set_request(*session,
 
506
                                        GNUTLS_CERT_IGNORE);
345
507
  
346
 
  gnutls_dh_set_prime_bits (es->session, DH_BITS);
 
508
  gnutls_dh_set_prime_bits(*session, mc.dh_bits);
347
509
  
348
510
  return 0;
349
511
}
350
512
 
 
513
/* Avahi log function callback */
351
514
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
352
515
                      __attribute__((unused)) const char *txt){}
353
516
 
 
517
/* Called when a Mandos server is found */
354
518
static int start_mandos_communication(const char *ip, uint16_t port,
355
519
                                      AvahiIfIndex if_index,
356
 
                                      mandos_context *mc){
 
520
                                      int af){
357
521
  int ret, tcp_sd;
358
 
  struct sockaddr_in6 to;
359
 
  encrypted_session es;
 
522
  ssize_t sret;
 
523
  union {
 
524
    struct sockaddr_in in;
 
525
    struct sockaddr_in6 in6;
 
526
  } to;
360
527
  char *buffer = NULL;
361
528
  char *decrypted_buffer;
362
529
  size_t buffer_length = 0;
363
530
  size_t buffer_capacity = 0;
364
531
  ssize_t decrypted_buffer_size;
365
 
  size_t written = 0;
 
532
  size_t written;
366
533
  int retval = 0;
367
 
  char interface[IF_NAMESIZE];
 
534
  gnutls_session_t session;
 
535
  int pf;                       /* Protocol family */
 
536
  
 
537
  switch(af){
 
538
  case AF_INET6:
 
539
    pf = PF_INET6;
 
540
    break;
 
541
  case AF_INET:
 
542
    pf = PF_INET;
 
543
    break;
 
544
  default:
 
545
    fprintf(stderr, "Bad address family: %d\n", af);
 
546
    return -1;
 
547
  }
 
548
  
 
549
  ret = init_gnutls_session(&session);
 
550
  if(ret != 0){
 
551
    return -1;
 
552
  }
368
553
  
369
554
  if(debug){
370
 
    fprintf(stderr, "Setting up a tcp connection to %s, port %d\n",
371
 
            ip, port);
 
555
    fprintf(stderr, "Setting up a TCP connection to %s, port %" PRIu16
 
556
            "\n", ip, port);
372
557
  }
373
558
  
374
 
  tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
375
 
  if(tcp_sd < 0) {
 
559
  tcp_sd = socket(pf, SOCK_STREAM, 0);
 
560
  if(tcp_sd < 0){
376
561
    perror("socket");
377
562
    return -1;
378
563
  }
379
 
 
380
 
  if(debug){
381
 
    if(if_indextoname((unsigned int)if_index, interface) == NULL){
382
 
      if(debug){
383
 
        perror("if_indextoname");
384
 
      }
385
 
      return -1;
386
 
    }
387
 
    
388
 
    fprintf(stderr, "Binding to interface %s\n", interface);
389
 
  }
390
564
  
391
 
  memset(&to,0,sizeof(to));     /* Spurious warning */
392
 
  to.sin6_family = AF_INET6;
393
 
  ret = inet_pton(AF_INET6, ip, &to.sin6_addr);
394
 
  if (ret < 0 ){
 
565
  memset(&to, 0, sizeof(to));
 
566
  if(af == AF_INET6){
 
567
    to.in6.sin6_family = (sa_family_t)af;
 
568
    ret = inet_pton(af, ip, &to.in6.sin6_addr);
 
569
  } else {                      /* IPv4 */
 
570
    to.in.sin_family = (sa_family_t)af;
 
571
    ret = inet_pton(af, ip, &to.in.sin_addr);
 
572
  }
 
573
  if(ret < 0 ){
395
574
    perror("inet_pton");
396
575
    return -1;
397
 
  }  
 
576
  }
398
577
  if(ret == 0){
399
578
    fprintf(stderr, "Bad address: %s\n", ip);
400
579
    return -1;
401
580
  }
402
 
  to.sin6_port = htons(port);   /* Spurious warning */
403
 
  
404
 
  to.sin6_scope_id = (uint32_t)if_index;
 
581
  if(af == AF_INET6){
 
582
    to.in6.sin6_port = htons(port); /* Spurious warnings from
 
583
                                       -Wconversion and
 
584
                                       -Wunreachable-code */
 
585
    
 
586
    if(IN6_IS_ADDR_LINKLOCAL /* Spurious warnings from */
 
587
       (&to.in6.sin6_addr)){ /* -Wstrict-aliasing=2 or lower and
 
588
                              -Wunreachable-code*/
 
589
      if(if_index == AVAHI_IF_UNSPEC){
 
590
        fprintf(stderr, "An IPv6 link-local address is incomplete"
 
591
                " without a network interface\n");
 
592
        return -1;
 
593
      }
 
594
      /* Set the network interface number as scope */
 
595
      to.in6.sin6_scope_id = (uint32_t)if_index;
 
596
    }
 
597
  } else {
 
598
    to.in.sin_port = htons(port); /* Spurious warnings from
 
599
                                     -Wconversion and
 
600
                                     -Wunreachable-code */
 
601
  }
405
602
  
406
603
  if(debug){
407
 
    fprintf(stderr, "Connection to: %s, port %d\n", ip, port);
408
 
/*     char addrstr[INET6_ADDRSTRLEN]; */
409
 
/*     if(inet_ntop(to.sin6_family, &(to.sin6_addr), addrstr, */
410
 
/*               sizeof(addrstr)) == NULL){ */
411
 
/*       perror("inet_ntop"); */
412
 
/*     } else { */
413
 
/*       fprintf(stderr, "Really connecting to: %s, port %d\n", */
414
 
/*            addrstr, ntohs(to.sin6_port)); */
415
 
/*     } */
 
604
    if(af == AF_INET6 and if_index != AVAHI_IF_UNSPEC){
 
605
      char interface[IF_NAMESIZE];
 
606
      if(if_indextoname((unsigned int)if_index, interface) == NULL){
 
607
        perror("if_indextoname");
 
608
      } else {
 
609
        fprintf(stderr, "Connection to: %s%%%s, port %" PRIu16 "\n",
 
610
                ip, interface, port);
 
611
      }
 
612
    } else {
 
613
      fprintf(stderr, "Connection to: %s, port %" PRIu16 "\n", ip,
 
614
              port);
 
615
    }
 
616
    char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ?
 
617
                 INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = "";
 
618
    const char *pcret;
 
619
    if(af == AF_INET6){
 
620
      pcret = inet_ntop(af, &(to.in6.sin6_addr), addrstr,
 
621
                        sizeof(addrstr));
 
622
    } else {
 
623
      pcret = inet_ntop(af, &(to.in.sin_addr), addrstr,
 
624
                        sizeof(addrstr));
 
625
    }
 
626
    if(pcret == NULL){
 
627
      perror("inet_ntop");
 
628
    } else {
 
629
      if(strcmp(addrstr, ip) != 0){
 
630
        fprintf(stderr, "Canonical address form: %s\n", addrstr);
 
631
      }
 
632
    }
416
633
  }
417
634
  
418
 
  ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
419
 
  if (ret < 0){
 
635
  if(af == AF_INET6){
 
636
    ret = connect(tcp_sd, &to.in6, sizeof(to));
 
637
  } else {
 
638
    ret = connect(tcp_sd, &to.in, sizeof(to)); /* IPv4 */
 
639
  }
 
640
  if(ret < 0){
420
641
    perror("connect");
421
642
    return -1;
422
643
  }
423
644
  
424
 
  ret = initgnutls (&es);
425
 
  if (ret != 0){
426
 
    retval = -1;
427
 
    return -1;
 
645
  const char *out = mandos_protocol_version;
 
646
  written = 0;
 
647
  while(true){
 
648
    size_t out_size = strlen(out);
 
649
    ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
 
650
                                   out_size - written));
 
651
    if(ret == -1){
 
652
      perror("write");
 
653
      retval = -1;
 
654
      goto mandos_end;
 
655
    }
 
656
    written += (size_t)ret;
 
657
    if(written < out_size){
 
658
      continue;
 
659
    } else {
 
660
      if(out == mandos_protocol_version){
 
661
        written = 0;
 
662
        out = "\r\n";
 
663
      } else {
 
664
        break;
 
665
      }
 
666
    }
428
667
  }
429
668
  
430
 
  gnutls_transport_set_ptr (es.session,
431
 
                            (gnutls_transport_ptr_t) tcp_sd);
432
 
  
433
669
  if(debug){
434
670
    fprintf(stderr, "Establishing TLS session with %s\n", ip);
435
671
  }
436
672
  
437
 
  ret = gnutls_handshake (es.session);
438
 
  
439
 
  if (ret != GNUTLS_E_SUCCESS){
 
673
  gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t) tcp_sd);
 
674
  
 
675
  do{
 
676
    ret = gnutls_handshake(session);
 
677
  } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
 
678
  
 
679
  if(ret != GNUTLS_E_SUCCESS){
440
680
    if(debug){
441
 
      fprintf(stderr, "\n*** Handshake failed ***\n");
442
 
      gnutls_perror (ret);
 
681
      fprintf(stderr, "*** GnuTLS Handshake failed ***\n");
 
682
      gnutls_perror(ret);
443
683
    }
444
684
    retval = -1;
445
 
    goto exit;
 
685
    goto mandos_end;
446
686
  }
447
687
  
448
 
  //Retrieve OpenPGP packet that contains the wanted password
 
688
  /* Read OpenPGP packet that contains the wanted password */
449
689
  
450
690
  if(debug){
451
 
    fprintf(stderr, "Retrieving pgp encrypted password from %s\n",
 
691
    fprintf(stderr, "Retrieving OpenPGP encrypted password from %s\n",
452
692
            ip);
453
693
  }
454
 
 
 
694
  
455
695
  while(true){
456
 
    if (buffer_length + BUFFER_SIZE > buffer_capacity){
457
 
      buffer = realloc(buffer, buffer_capacity + BUFFER_SIZE);
458
 
      if (buffer == NULL){
459
 
        perror("realloc");
460
 
        goto exit;
461
 
      }
462
 
      buffer_capacity += BUFFER_SIZE;
 
696
    buffer_capacity = incbuffer(&buffer, buffer_length,
 
697
                                   buffer_capacity);
 
698
    if(buffer_capacity == 0){
 
699
      perror("incbuffer");
 
700
      retval = -1;
 
701
      goto mandos_end;
463
702
    }
464
703
    
465
 
    ret = gnutls_record_recv
466
 
      (es.session, buffer+buffer_length, BUFFER_SIZE);
467
 
    if (ret == 0){
 
704
    sret = gnutls_record_recv(session, buffer+buffer_length,
 
705
                              BUFFER_SIZE);
 
706
    if(sret == 0){
468
707
      break;
469
708
    }
470
 
    if (ret < 0){
471
 
      switch(ret){
 
709
    if(sret < 0){
 
710
      switch(sret){
472
711
      case GNUTLS_E_INTERRUPTED:
473
712
      case GNUTLS_E_AGAIN:
474
713
        break;
475
714
      case GNUTLS_E_REHANDSHAKE:
476
 
        ret = gnutls_handshake (es.session);
477
 
        if (ret < 0){
478
 
          fprintf(stderr, "\n*** Handshake failed ***\n");
479
 
          gnutls_perror (ret);
 
715
        do{
 
716
          ret = gnutls_handshake(session);
 
717
        } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
 
718
        if(ret < 0){
 
719
          fprintf(stderr, "*** GnuTLS Re-handshake failed ***\n");
 
720
          gnutls_perror(ret);
480
721
          retval = -1;
481
 
          goto exit;
 
722
          goto mandos_end;
482
723
        }
483
724
        break;
484
725
      default:
485
726
        fprintf(stderr, "Unknown error while reading data from"
486
 
                " encrypted session with mandos server\n");
 
727
                " encrypted session with Mandos server\n");
487
728
        retval = -1;
488
 
        gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
489
 
        goto exit;
 
729
        gnutls_bye(session, GNUTLS_SHUT_RDWR);
 
730
        goto mandos_end;
490
731
      }
491
732
    } else {
492
 
      buffer_length += (size_t) ret;
 
733
      buffer_length += (size_t) sret;
493
734
    }
494
735
  }
495
736
  
496
 
  if (buffer_length > 0){
 
737
  if(debug){
 
738
    fprintf(stderr, "Closing TLS session\n");
 
739
  }
 
740
  
 
741
  gnutls_bye(session, GNUTLS_SHUT_RDWR);
 
742
  
 
743
  if(buffer_length > 0){
497
744
    decrypted_buffer_size = pgp_packet_decrypt(buffer,
498
745
                                               buffer_length,
499
 
                                               &decrypted_buffer,
500
 
                                               certdir);
501
 
    if (decrypted_buffer_size >= 0){
 
746
                                               &decrypted_buffer);
 
747
    if(decrypted_buffer_size >= 0){
 
748
      written = 0;
502
749
      while(written < (size_t) decrypted_buffer_size){
503
 
        ret = (int)fwrite (decrypted_buffer + written, 1,
504
 
                           (size_t)decrypted_buffer_size - written,
505
 
                           stdout);
 
750
        ret = (int)fwrite(decrypted_buffer + written, 1,
 
751
                          (size_t)decrypted_buffer_size - written,
 
752
                          stdout);
506
753
        if(ret == 0 and ferror(stdout)){
507
754
          if(debug){
508
755
            fprintf(stderr, "Error writing encrypted data: %s\n",
517
764
    } else {
518
765
      retval = -1;
519
766
    }
520
 
  }
521
 
 
522
 
  //shutdown procedure
523
 
 
524
 
  if(debug){
525
 
    fprintf(stderr, "Closing TLS session\n");
526
 
  }
527
 
 
 
767
  } else {
 
768
    retval = -1;
 
769
  }
 
770
  
 
771
  /* Shutdown procedure */
 
772
  
 
773
 mandos_end:
528
774
  free(buffer);
529
 
  gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
530
 
 exit:
531
 
  close(tcp_sd);
532
 
  gnutls_deinit (es.session);
533
 
  gnutls_certificate_free_credentials (es.cred);
534
 
  gnutls_global_deinit ();
 
775
  ret = (int)TEMP_FAILURE_RETRY(close(tcp_sd));
 
776
  if(ret == -1){
 
777
    perror("close");
 
778
  }
 
779
  gnutls_deinit(session);
535
780
  return retval;
536
781
}
537
782
 
538
 
static void resolve_callback( AvahiSServiceResolver *r,
539
 
                              AvahiIfIndex interface,
540
 
                              AVAHI_GCC_UNUSED AvahiProtocol protocol,
541
 
                              AvahiResolverEvent event,
542
 
                              const char *name,
543
 
                              const char *type,
544
 
                              const char *domain,
545
 
                              const char *host_name,
546
 
                              const AvahiAddress *address,
547
 
                              uint16_t port,
548
 
                              AVAHI_GCC_UNUSED AvahiStringList *txt,
549
 
                              AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
550
 
                              AVAHI_GCC_UNUSED void* userdata) {
551
 
  mandos_context *mc = userdata;
552
 
  assert(r);                    /* Spurious warning */
 
783
sig_atomic_t quit_now = 0;
 
784
 
 
785
static void resolve_callback(AvahiSServiceResolver *r,
 
786
                             AvahiIfIndex interface,
 
787
                             AvahiProtocol proto,
 
788
                             AvahiResolverEvent event,
 
789
                             const char *name,
 
790
                             const char *type,
 
791
                             const char *domain,
 
792
                             const char *host_name,
 
793
                             const AvahiAddress *address,
 
794
                             uint16_t port,
 
795
                             AVAHI_GCC_UNUSED AvahiStringList *txt,
 
796
                             AVAHI_GCC_UNUSED AvahiLookupResultFlags
 
797
                             flags,
 
798
                             AVAHI_GCC_UNUSED void* userdata){
 
799
  assert(r);
553
800
  
554
801
  /* Called whenever a service has been resolved successfully or
555
802
     timed out */
556
803
  
557
 
  switch (event) {
 
804
  if(quit_now){
 
805
    return;
 
806
  }
 
807
  
 
808
  switch(event){
558
809
  default:
559
810
  case AVAHI_RESOLVER_FAILURE:
560
 
    fprintf(stderr, "(Resolver) Failed to resolve service '%s' of"
561
 
            " type '%s' in domain '%s': %s\n", name, type, domain,
562
 
            avahi_strerror(avahi_server_errno(mc->server)));
 
811
    fprintf(stderr, "(Avahi Resolver) Failed to resolve service '%s'"
 
812
            " of type '%s' in domain '%s': %s\n", name, type, domain,
 
813
            avahi_strerror(avahi_server_errno(mc.server)));
563
814
    break;
564
815
    
565
816
  case AVAHI_RESOLVER_FOUND:
567
818
      char ip[AVAHI_ADDRESS_STR_MAX];
568
819
      avahi_address_snprint(ip, sizeof(ip), address);
569
820
      if(debug){
570
 
        fprintf(stderr, "Mandos server \"%s\" found on %s (%s) on"
571
 
                " port %d\n", name, host_name, ip, port);
 
821
        fprintf(stderr, "Mandos server \"%s\" found on %s (%s, %"
 
822
                PRIdMAX ") on port %" PRIu16 "\n", name, host_name,
 
823
                ip, (intmax_t)interface, port);
572
824
      }
573
 
      int ret = start_mandos_communication(ip, port, interface, mc);
574
 
      if (ret == 0){
575
 
        exit(EXIT_SUCCESS);
 
825
      int ret = start_mandos_communication(ip, port, interface,
 
826
                                           avahi_proto_to_af(proto));
 
827
      if(ret == 0){
 
828
        avahi_simple_poll_quit(mc.simple_poll);
576
829
      }
577
830
    }
578
831
  }
579
832
  avahi_s_service_resolver_free(r);
580
833
}
581
834
 
582
 
static void browse_callback( 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
 
  mandos_context *mc = userdata;
592
 
  assert(b);                    /* Spurious warning */
 
835
static void browse_callback(AvahiSServiceBrowser *b,
 
836
                            AvahiIfIndex interface,
 
837
                            AvahiProtocol protocol,
 
838
                            AvahiBrowserEvent event,
 
839
                            const char *name,
 
840
                            const char *type,
 
841
                            const char *domain,
 
842
                            AVAHI_GCC_UNUSED AvahiLookupResultFlags
 
843
                            flags,
 
844
                            AVAHI_GCC_UNUSED void* userdata){
 
845
  assert(b);
593
846
  
594
847
  /* Called whenever a new services becomes available on the LAN or
595
848
     is removed from the LAN */
596
849
  
597
 
  switch (event) {
 
850
  switch(event){
598
851
  default:
599
852
  case AVAHI_BROWSER_FAILURE:
600
 
      
601
 
    fprintf(stderr, "(Browser) %s\n",
602
 
            avahi_strerror(avahi_server_errno(mc->server)));
603
 
    avahi_simple_poll_quit(mc->simple_poll);
 
853
    
 
854
    fprintf(stderr, "(Avahi browser) %s\n",
 
855
            avahi_strerror(avahi_server_errno(mc.server)));
 
856
    avahi_simple_poll_quit(mc.simple_poll);
604
857
    return;
605
 
      
 
858
    
606
859
  case AVAHI_BROWSER_NEW:
607
 
    /* We ignore the returned resolver object. In the callback
608
 
       function we free it. If the server is terminated before
609
 
       the callback function is called the server will free
610
 
       the resolver for us. */
611
 
      
612
 
    if (!(avahi_s_service_resolver_new(mc->server, interface, protocol, name,
613
 
                                       type, domain,
614
 
                                       AVAHI_PROTO_INET6, 0,
615
 
                                       resolve_callback, mc)))
616
 
      fprintf(stderr, "Failed to resolve service '%s': %s\n", name,
617
 
              avahi_strerror(avahi_server_errno(s)));
 
860
    /* We ignore the returned Avahi resolver object. In the callback
 
861
       function we free it. If the Avahi server is terminated before
 
862
       the callback function is called the Avahi server will free the
 
863
       resolver for us. */
 
864
    
 
865
    if(avahi_s_service_resolver_new(mc.server, interface, protocol,
 
866
                                    name, type, domain, protocol, 0,
 
867
                                    resolve_callback, NULL) == NULL)
 
868
      fprintf(stderr, "Avahi: Failed to resolve service '%s': %s\n",
 
869
              name, avahi_strerror(avahi_server_errno(mc.server)));
618
870
    break;
619
 
      
 
871
    
620
872
  case AVAHI_BROWSER_REMOVE:
621
873
    break;
622
 
      
 
874
    
623
875
  case AVAHI_BROWSER_ALL_FOR_NOW:
624
876
  case AVAHI_BROWSER_CACHE_EXHAUSTED:
 
877
    if(debug){
 
878
      fprintf(stderr, "No Mandos server found, still searching...\n");
 
879
    }
625
880
    break;
626
881
  }
627
882
}
628
883
 
629
 
/* Combines file name and path and returns the malloced new
630
 
   string. some sane checks could/should be added */
631
 
static const char *combinepath(const char *first, const char *second){
632
 
  size_t f_len = strlen(first);
633
 
  size_t s_len = strlen(second);
634
 
  char *tmp = malloc(f_len + s_len + 2);
635
 
  if (tmp == NULL){
636
 
    return NULL;
637
 
  }
638
 
  if(f_len > 0){
639
 
    memcpy(tmp, first, f_len);
640
 
  }
641
 
  tmp[f_len] = '/';
642
 
  if(s_len > 0){
643
 
    memcpy(tmp + f_len + 1, second, s_len);
644
 
  }
645
 
  tmp[f_len + 1 + s_len] = '\0';
646
 
  return tmp;
 
884
/* stop main loop after sigterm has been called */
 
885
static void handle_sigterm(__attribute__((unused)) int sig){
 
886
  if(quit_now){
 
887
    return;
 
888
  }
 
889
  quit_now = 1;
 
890
  int old_errno = errno;
 
891
  if(mc.simple_poll != NULL){
 
892
    avahi_simple_poll_quit(mc.simple_poll);
 
893
  }
 
894
  errno = old_errno;
647
895
}
648
896
 
649
 
 
650
 
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
651
 
    AvahiServerConfig config;
652
 
    AvahiSServiceBrowser *sb = NULL;
653
 
    int error;
654
 
    int ret;
655
 
    int returncode = EXIT_SUCCESS;
656
 
    const char *interface = "eth0";
657
 
    struct ifreq network;
658
 
    int sd;
659
 
    char *connect_to = NULL;
660
 
    AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
661
 
    mandos_context mc = { .simple_poll = NULL, .server = NULL,
662
 
                          .dh_bits = 2048, .priority = "SECURE256"};
 
897
int main(int argc, char *argv[]){
 
898
  AvahiSServiceBrowser *sb = NULL;
 
899
  int error;
 
900
  int ret;
 
901
  intmax_t tmpmax;
 
902
  char *tmp;
 
903
  int exitcode = EXIT_SUCCESS;
 
904
  const char *interface = "eth0";
 
905
  struct ifreq network;
 
906
  int sd = -1;
 
907
  bool take_down_interface = false;
 
908
  uid_t uid;
 
909
  gid_t gid;
 
910
  char *connect_to = NULL;
 
911
  char tempdir[] = "/tmp/mandosXXXXXX";
 
912
  bool tempdir_created = false;
 
913
  AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
 
914
  const char *seckey = PATHDIR "/" SECKEY;
 
915
  const char *pubkey = PATHDIR "/" PUBKEY;
 
916
  
 
917
  bool gnutls_initialized = false;
 
918
  bool gpgme_initialized = false;
 
919
  float delay = 2.5f;
 
920
  
 
921
  struct sigaction old_sigterm_action;
 
922
  struct sigaction sigterm_action = { .sa_handler = handle_sigterm };
 
923
  
 
924
  {
 
925
    struct argp_option options[] = {
 
926
      { .name = "debug", .key = 128,
 
927
        .doc = "Debug mode", .group = 3 },
 
928
      { .name = "connect", .key = 'c',
 
929
        .arg = "ADDRESS:PORT",
 
930
        .doc = "Connect directly to a specific Mandos server",
 
931
        .group = 1 },
 
932
      { .name = "interface", .key = 'i',
 
933
        .arg = "NAME",
 
934
        .doc = "Network interface that will be used to search for"
 
935
        " Mandos servers",
 
936
        .group = 1 },
 
937
      { .name = "seckey", .key = 's',
 
938
        .arg = "FILE",
 
939
        .doc = "OpenPGP secret key file base name",
 
940
        .group = 1 },
 
941
      { .name = "pubkey", .key = 'p',
 
942
        .arg = "FILE",
 
943
        .doc = "OpenPGP public key file base name",
 
944
        .group = 2 },
 
945
      { .name = "dh-bits", .key = 129,
 
946
        .arg = "BITS",
 
947
        .doc = "Bit length of the prime number used in the"
 
948
        " Diffie-Hellman key exchange",
 
949
        .group = 2 },
 
950
      { .name = "priority", .key = 130,
 
951
        .arg = "STRING",
 
952
        .doc = "GnuTLS priority string for the TLS handshake",
 
953
        .group = 1 },
 
954
      { .name = "delay", .key = 131,
 
955
        .arg = "SECONDS",
 
956
        .doc = "Maximum delay to wait for interface startup",
 
957
        .group = 2 },
 
958
      { .name = NULL }
 
959
    };
663
960
    
664
 
    while (true){
665
 
      static struct option long_options[] = {
666
 
        {"debug", no_argument, (int *)&debug, 1},
667
 
        {"connect", required_argument, 0, 'C'},
668
 
        {"interface", required_argument, 0, 'i'},
669
 
        {"certdir", required_argument, 0, 'd'},
670
 
        {"certkey", required_argument, 0, 'c'},
671
 
        {"certfile", required_argument, 0, 'k'},
672
 
        {"dh_bits", required_argument, 0, 'D'},
673
 
        {"priority", required_argument, 0, 'p'},
674
 
        {0, 0, 0, 0} };
675
 
      
676
 
      int option_index = 0;
677
 
      ret = getopt_long (argc, argv, "i:", long_options,
678
 
                         &option_index);
679
 
      
680
 
      if (ret == -1){
681
 
        break;
682
 
      }
683
 
      
684
 
      switch(ret){
685
 
      case 0:
686
 
        break;
687
 
      case 'i':
688
 
        interface = optarg;
689
 
        break;
690
 
      case 'C':
691
 
        connect_to = optarg;
692
 
        break;
693
 
      case 'd':
694
 
        certdir = optarg;
695
 
        break;
696
 
      case 'c':
697
 
        certfile = optarg;
698
 
        break;
699
 
      case 'k':
700
 
        certkey = optarg;
701
 
        break;
702
 
      case 'D':
703
 
        {
704
 
          long int tmp;
705
 
          errno = 0;
706
 
          tmp = strtol(optarg, NULL, 10);
707
 
          if (errno == ERANGE){
708
 
            perror("strtol");
709
 
            exit(EXIT_FAILURE);
710
 
          }
711
 
          mc.dh_bits = tmp;
712
 
        }
713
 
        break;
714
 
      case 'p':
715
 
        mc.priority = optarg;
 
961
    error_t parse_opt(int key, char *arg,
 
962
                      struct argp_state *state){
 
963
      switch(key){
 
964
      case 128:                 /* --debug */
 
965
        debug = true;
 
966
        break;
 
967
      case 'c':                 /* --connect */
 
968
        connect_to = arg;
 
969
        break;
 
970
      case 'i':                 /* --interface */
 
971
        interface = arg;
 
972
        break;
 
973
      case 's':                 /* --seckey */
 
974
        seckey = arg;
 
975
        break;
 
976
      case 'p':                 /* --pubkey */
 
977
        pubkey = arg;
 
978
        break;
 
979
      case 129:                 /* --dh-bits */
 
980
        errno = 0;
 
981
        tmpmax = strtoimax(arg, &tmp, 10);
 
982
        if(errno != 0 or tmp == arg or *tmp != '\0'
 
983
           or tmpmax != (typeof(mc.dh_bits))tmpmax){
 
984
          fprintf(stderr, "Bad number of DH bits\n");
 
985
          exit(EXIT_FAILURE);
 
986
        }
 
987
        mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
 
988
        break;
 
989
      case 130:                 /* --priority */
 
990
        mc.priority = arg;
 
991
        break;
 
992
      case 131:                 /* --delay */
 
993
        errno = 0;
 
994
        delay = strtof(arg, &tmp);
 
995
        if(errno != 0 or tmp == arg or *tmp != '\0'){
 
996
          fprintf(stderr, "Bad delay\n");
 
997
          exit(EXIT_FAILURE);
 
998
        }
 
999
        break;
 
1000
      case ARGP_KEY_ARG:
 
1001
        argp_usage(state);
 
1002
      case ARGP_KEY_END:
716
1003
        break;
717
1004
      default:
718
 
        exit(EXIT_FAILURE);
 
1005
        return ARGP_ERR_UNKNOWN;
719
1006
      }
720
 
    }
721
 
    
722
 
    certfile = combinepath(certdir, certfile);
723
 
    if (certfile == NULL){
724
 
      perror("combinepath");
725
 
      returncode = EXIT_FAILURE;
726
 
      goto exit;
727
 
    }
728
 
 
729
 
    certkey = combinepath(certdir, certkey);
730
 
    if (certkey == NULL){
731
 
      perror("combinepath");
732
 
      returncode = EXIT_FAILURE;
733
 
      goto exit;
734
 
    }
735
 
    
 
1007
      return 0;
 
1008
    }
 
1009
    
 
1010
    struct argp argp = { .options = options, .parser = parse_opt,
 
1011
                         .args_doc = "",
 
1012
                         .doc = "Mandos client -- Get and decrypt"
 
1013
                         " passwords from a Mandos server" };
 
1014
    ret = argp_parse(&argp, argc, argv, 0, 0, NULL);
 
1015
    if(ret == ARGP_ERR_UNKNOWN){
 
1016
      fprintf(stderr, "Unknown error while parsing arguments\n");
 
1017
      exitcode = EXIT_FAILURE;
 
1018
      goto end;
 
1019
    }
 
1020
  }
 
1021
  
 
1022
  if(not debug){
 
1023
    avahi_set_log_function(empty_log);
 
1024
  }
 
1025
  
 
1026
  /* Initialize Avahi early so avahi_simple_poll_quit() can be called
 
1027
     from the signal handler */
 
1028
  /* Initialize the pseudo-RNG for Avahi */
 
1029
  srand((unsigned int) time(NULL));
 
1030
  mc.simple_poll = avahi_simple_poll_new();
 
1031
  if(mc.simple_poll == NULL){
 
1032
    fprintf(stderr, "Avahi: Failed to create simple poll object.\n");
 
1033
    exitcode = EXIT_FAILURE;
 
1034
    goto end;
 
1035
  }
 
1036
  
 
1037
  sigemptyset(&sigterm_action.sa_mask);
 
1038
  ret = sigaddset(&sigterm_action.sa_mask, SIGINT);
 
1039
  if(ret == -1){
 
1040
    perror("sigaddset");
 
1041
    exitcode = EXIT_FAILURE;
 
1042
    goto end;
 
1043
  }
 
1044
  ret = sigaddset(&sigterm_action.sa_mask, SIGHUP);
 
1045
  if(ret == -1){
 
1046
    perror("sigaddset");
 
1047
    exitcode = EXIT_FAILURE;
 
1048
    goto end;
 
1049
  }
 
1050
  ret = sigaddset(&sigterm_action.sa_mask, SIGTERM);
 
1051
  if(ret == -1){
 
1052
    perror("sigaddset");
 
1053
    exitcode = EXIT_FAILURE;
 
1054
    goto end;
 
1055
  }
 
1056
  ret = sigaction(SIGTERM, &sigterm_action, &old_sigterm_action);
 
1057
  if(ret == -1){
 
1058
    perror("sigaction");
 
1059
    exitcode = EXIT_FAILURE;
 
1060
    goto end;
 
1061
  }  
 
1062
  
 
1063
  /* If the interface is down, bring it up */
 
1064
  if(interface[0] != '\0'){
736
1065
    if_index = (AvahiIfIndex) if_nametoindex(interface);
737
1066
    if(if_index == 0){
738
1067
      fprintf(stderr, "No such interface: \"%s\"\n", interface);
739
 
      exit(EXIT_FAILURE);
740
 
    }
741
 
    
742
 
    if(connect_to != NULL){
743
 
      /* Connect directly, do not use Zeroconf */
744
 
      /* (Mainly meant for debugging) */
745
 
      char *address = strrchr(connect_to, ':');
746
 
      if(address == NULL){
747
 
        fprintf(stderr, "No colon in address\n");
748
 
        exit(EXIT_FAILURE);
749
 
      }
750
 
      errno = 0;
751
 
      uint16_t port = (uint16_t) strtol(address+1, NULL, 10);
752
 
      if(errno){
753
 
        perror("Bad port number");
754
 
        exit(EXIT_FAILURE);
755
 
      }
756
 
      *address = '\0';
757
 
      address = connect_to;
758
 
      ret = start_mandos_communication(address, port, if_index);
759
 
      if(ret < 0){
760
 
        exit(EXIT_FAILURE);
761
 
      } else {
762
 
        exit(EXIT_SUCCESS);
763
 
      }
764
 
    }
 
1068
      exitcode = EXIT_FAILURE;
 
1069
      goto end;
 
1070
    }
 
1071
    
 
1072
    if(quit_now){
 
1073
      goto end;
 
1074
    }
 
1075
    
 
1076
#ifdef __linux__
 
1077
    /* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
 
1078
       messages to mess up the prompt */
 
1079
    ret = klogctl(8, NULL, 5);
 
1080
    bool restore_loglevel = true;
 
1081
    if(ret == -1){
 
1082
      restore_loglevel = false;
 
1083
      perror("klogctl");
 
1084
    }
 
1085
#endif  /* __linux__ */
765
1086
    
766
1087
    sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
767
 
    if(sd < 0) {
 
1088
    if(sd < 0){
768
1089
      perror("socket");
769
 
      returncode = EXIT_FAILURE;
770
 
      goto exit;
 
1090
      exitcode = EXIT_FAILURE;
 
1091
#ifdef __linux__
 
1092
      if(restore_loglevel){
 
1093
        ret = klogctl(7, NULL, 0);
 
1094
        if(ret == -1){
 
1095
          perror("klogctl");
 
1096
        }
 
1097
      }
 
1098
#endif  /* __linux__ */
 
1099
      goto end;
771
1100
    }
772
 
    strcpy(network.ifr_name, interface);    
 
1101
    strcpy(network.ifr_name, interface);
773
1102
    ret = ioctl(sd, SIOCGIFFLAGS, &network);
774
1103
    if(ret == -1){
775
 
      
776
1104
      perror("ioctl SIOCGIFFLAGS");
777
 
      returncode = EXIT_FAILURE;
778
 
      goto exit;
 
1105
#ifdef __linux__
 
1106
      if(restore_loglevel){
 
1107
        ret = klogctl(7, NULL, 0);
 
1108
        if(ret == -1){
 
1109
          perror("klogctl");
 
1110
        }
 
1111
      }
 
1112
#endif  /* __linux__ */
 
1113
      exitcode = EXIT_FAILURE;
 
1114
      goto end;
779
1115
    }
780
1116
    if((network.ifr_flags & IFF_UP) == 0){
781
1117
      network.ifr_flags |= IFF_UP;
 
1118
      take_down_interface = true;
782
1119
      ret = ioctl(sd, SIOCSIFFLAGS, &network);
783
1120
      if(ret == -1){
 
1121
        take_down_interface = false;
784
1122
        perror("ioctl SIOCSIFFLAGS");
785
 
        returncode = EXIT_FAILURE;
786
 
        goto exit;
787
 
      }
788
 
    }
789
 
    close(sd);
790
 
    
791
 
    if (not debug){
792
 
      avahi_set_log_function(empty_log);
793
 
    }
794
 
    
795
 
    /* Initialize the psuedo-RNG */
796
 
    srand((unsigned int) time(NULL));
797
 
 
798
 
    /* Allocate main loop object */
799
 
    if (!(mc.simple_poll = avahi_simple_poll_new())) {
800
 
        fprintf(stderr, "Failed to create simple poll object.\n");
801
 
        returncode = EXIT_FAILURE;      
802
 
        goto exit;
803
 
    }
804
 
 
805
 
    /* Do not publish any local records */
 
1123
        exitcode = EXIT_FAILURE;
 
1124
#ifdef __linux__
 
1125
        if(restore_loglevel){
 
1126
          ret = klogctl(7, NULL, 0);
 
1127
          if(ret == -1){
 
1128
            perror("klogctl");
 
1129
          }
 
1130
        }
 
1131
#endif  /* __linux__ */
 
1132
        goto end;
 
1133
      }
 
1134
    }
 
1135
    /* sleep checking until interface is running */
 
1136
    for(int i=0; i < delay * 4; i++){
 
1137
      ret = ioctl(sd, SIOCGIFFLAGS, &network);
 
1138
      if(ret == -1){
 
1139
        perror("ioctl SIOCGIFFLAGS");
 
1140
      } else if(network.ifr_flags & IFF_RUNNING){
 
1141
        break;
 
1142
      }
 
1143
      struct timespec sleeptime = { .tv_nsec = 250000000 };
 
1144
      ret = nanosleep(&sleeptime, NULL);
 
1145
      if(ret == -1 and errno != EINTR){
 
1146
        perror("nanosleep");
 
1147
      }
 
1148
    }
 
1149
    if(not take_down_interface){
 
1150
      /* We won't need the socket anymore */
 
1151
      ret = (int)TEMP_FAILURE_RETRY(close(sd));
 
1152
      if(ret == -1){
 
1153
        perror("close");
 
1154
      }
 
1155
    }
 
1156
#ifdef __linux__
 
1157
    if(restore_loglevel){
 
1158
      /* Restores kernel loglevel to default */
 
1159
      ret = klogctl(7, NULL, 0);
 
1160
      if(ret == -1){
 
1161
        perror("klogctl");
 
1162
      }
 
1163
    }
 
1164
#endif  /* __linux__ */
 
1165
  }
 
1166
  
 
1167
  if(quit_now){
 
1168
    goto end;
 
1169
  }
 
1170
  
 
1171
  uid = getuid();
 
1172
  gid = getgid();
 
1173
  
 
1174
  errno = 0;
 
1175
  setgid(gid);
 
1176
  if(ret == -1){
 
1177
    perror("setgid");
 
1178
  }
 
1179
  
 
1180
  ret = setuid(uid);
 
1181
  if(ret == -1){
 
1182
    perror("setuid");
 
1183
  }
 
1184
  
 
1185
  if(quit_now){
 
1186
    goto end;
 
1187
  }
 
1188
  
 
1189
  ret = init_gnutls_global(pubkey, seckey);
 
1190
  if(ret == -1){
 
1191
    fprintf(stderr, "init_gnutls_global failed\n");
 
1192
    exitcode = EXIT_FAILURE;
 
1193
    goto end;
 
1194
  } else {
 
1195
    gnutls_initialized = true;
 
1196
  }
 
1197
  
 
1198
  if(quit_now){
 
1199
    goto end;
 
1200
  }
 
1201
  
 
1202
  tempdir_created = true;
 
1203
  if(mkdtemp(tempdir) == NULL){
 
1204
    tempdir_created = false;
 
1205
    perror("mkdtemp");
 
1206
    goto end;
 
1207
  }
 
1208
  
 
1209
  if(quit_now){
 
1210
    goto end;
 
1211
  }
 
1212
  
 
1213
  if(not init_gpgme(pubkey, seckey, tempdir)){
 
1214
    fprintf(stderr, "init_gpgme failed\n");
 
1215
    exitcode = EXIT_FAILURE;
 
1216
    goto end;
 
1217
  } else {
 
1218
    gpgme_initialized = true;
 
1219
  }
 
1220
  
 
1221
  if(quit_now){
 
1222
    goto end;
 
1223
  }
 
1224
  
 
1225
  if(connect_to != NULL){
 
1226
    /* Connect directly, do not use Zeroconf */
 
1227
    /* (Mainly meant for debugging) */
 
1228
    char *address = strrchr(connect_to, ':');
 
1229
    if(address == NULL){
 
1230
      fprintf(stderr, "No colon in address\n");
 
1231
      exitcode = EXIT_FAILURE;
 
1232
      goto end;
 
1233
    }
 
1234
    
 
1235
    if(quit_now){
 
1236
      goto end;
 
1237
    }
 
1238
    
 
1239
    uint16_t port;
 
1240
    errno = 0;
 
1241
    tmpmax = strtoimax(address+1, &tmp, 10);
 
1242
    if(errno != 0 or tmp == address+1 or *tmp != '\0'
 
1243
       or tmpmax != (uint16_t)tmpmax){
 
1244
      fprintf(stderr, "Bad port number\n");
 
1245
      exitcode = EXIT_FAILURE;
 
1246
      goto end;
 
1247
    }
 
1248
  
 
1249
    if(quit_now){
 
1250
      goto end;
 
1251
    }
 
1252
    
 
1253
    port = (uint16_t)tmpmax;
 
1254
    *address = '\0';
 
1255
    address = connect_to;
 
1256
    /* Colon in address indicates IPv6 */
 
1257
    int af;
 
1258
    if(strchr(address, ':') != NULL){
 
1259
      af = AF_INET6;
 
1260
    } else {
 
1261
      af = AF_INET;
 
1262
    }
 
1263
    
 
1264
    if(quit_now){
 
1265
      goto end;
 
1266
    }
 
1267
    
 
1268
    ret = start_mandos_communication(address, port, if_index, af);
 
1269
    if(ret < 0){
 
1270
      exitcode = EXIT_FAILURE;
 
1271
    } else {
 
1272
      exitcode = EXIT_SUCCESS;
 
1273
    }
 
1274
    goto end;
 
1275
  }
 
1276
  
 
1277
  if(quit_now){
 
1278
    goto end;
 
1279
  }
 
1280
  
 
1281
  {
 
1282
    AvahiServerConfig config;
 
1283
    /* Do not publish any local Zeroconf records */
806
1284
    avahi_server_config_init(&config);
807
1285
    config.publish_hinfo = 0;
808
1286
    config.publish_addresses = 0;
809
1287
    config.publish_workstation = 0;
810
1288
    config.publish_domain = 0;
811
 
 
 
1289
    
812
1290
    /* Allocate a new server */
813
 
    mc.server = avahi_server_new(avahi_simple_poll_get(simple_poll),
814
 
                              &config, NULL, NULL, &error);
815
 
 
816
 
    /* Free the configuration data */
 
1291
    mc.server = avahi_server_new(avahi_simple_poll_get
 
1292
                                 (mc.simple_poll), &config, NULL,
 
1293
                                 NULL, &error);
 
1294
    
 
1295
    /* Free the Avahi configuration data */
817
1296
    avahi_server_config_free(&config);
818
 
 
819
 
    /* Check if creating the server object succeeded */
820
 
    if (!mc.server) {
821
 
        fprintf(stderr, "Failed to create server: %s\n",
822
 
                avahi_strerror(error));
823
 
        returncode = EXIT_FAILURE;
824
 
        goto exit;
825
 
    }
826
 
    
827
 
    /* Create the service browser */
828
 
    sb = avahi_s_service_browser_new(mc.server, if_index,
829
 
                                     AVAHI_PROTO_INET6,
830
 
                                     "_mandos._tcp", NULL, 0,
831
 
                                     browse_callback, &mc);
832
 
    if (!sb) {
833
 
        fprintf(stderr, "Failed to create service browser: %s\n",
834
 
                avahi_strerror(avahi_server_errno(mc.server)));
835
 
        returncode = EXIT_FAILURE;
836
 
        goto exit;
837
 
    }
838
 
    
839
 
    /* Run the main loop */
840
 
 
841
 
    if (debug){
842
 
      fprintf(stderr, "Starting avahi loop search\n");
843
 
    }
844
 
    
845
 
    avahi_simple_poll_loop(simple_poll);
846
 
    
847
 
 exit:
848
 
 
849
 
    if (debug){
850
 
      fprintf(stderr, "%s exiting\n", argv[0]);
851
 
    }
852
 
    
853
 
    /* Cleanup things */
854
 
    if (sb)
855
 
        avahi_s_service_browser_free(sb);
856
 
    
857
 
    if (mc.server)
858
 
        avahi_server_free(mc.server);
859
 
 
860
 
    if (simple_poll)
861
 
        avahi_simple_poll_free(simple_poll);
862
 
    free(certfile);
863
 
    free(certkey);
864
 
    
865
 
    return returncode;
 
1297
  }
 
1298
  
 
1299
  /* Check if creating the Avahi server object succeeded */
 
1300
  if(mc.server == NULL){
 
1301
    fprintf(stderr, "Failed to create Avahi server: %s\n",
 
1302
            avahi_strerror(error));
 
1303
    exitcode = EXIT_FAILURE;
 
1304
    goto end;
 
1305
  }
 
1306
  
 
1307
  if(quit_now){
 
1308
    goto end;
 
1309
  }
 
1310
  
 
1311
  /* Create the Avahi service browser */
 
1312
  sb = avahi_s_service_browser_new(mc.server, if_index,
 
1313
                                   AVAHI_PROTO_UNSPEC, "_mandos._tcp",
 
1314
                                   NULL, 0, browse_callback, NULL);
 
1315
  if(sb == NULL){
 
1316
    fprintf(stderr, "Failed to create service browser: %s\n",
 
1317
            avahi_strerror(avahi_server_errno(mc.server)));
 
1318
    exitcode = EXIT_FAILURE;
 
1319
    goto end;
 
1320
  }
 
1321
  
 
1322
  if(quit_now){
 
1323
    goto end;
 
1324
  }
 
1325
  
 
1326
  /* Run the main loop */
 
1327
  
 
1328
  if(debug){
 
1329
    fprintf(stderr, "Starting Avahi loop search\n");
 
1330
  }
 
1331
  
 
1332
  avahi_simple_poll_loop(mc.simple_poll);
 
1333
  
 
1334
 end:
 
1335
  
 
1336
  if(debug){
 
1337
    fprintf(stderr, "%s exiting\n", argv[0]);
 
1338
  }
 
1339
  
 
1340
  /* Cleanup things */
 
1341
  if(sb != NULL)
 
1342
    avahi_s_service_browser_free(sb);
 
1343
  
 
1344
  if(mc.server != NULL)
 
1345
    avahi_server_free(mc.server);
 
1346
  
 
1347
  if(mc.simple_poll != NULL)
 
1348
    avahi_simple_poll_free(mc.simple_poll);
 
1349
  
 
1350
  if(gnutls_initialized){
 
1351
    gnutls_certificate_free_credentials(mc.cred);
 
1352
    gnutls_global_deinit();
 
1353
    gnutls_dh_params_deinit(mc.dh_params);
 
1354
  }
 
1355
  
 
1356
  if(gpgme_initialized){
 
1357
    gpgme_release(mc.ctx);
 
1358
  }
 
1359
  
 
1360
  /* Take down the network interface */
 
1361
  if(take_down_interface){
 
1362
    ret = ioctl(sd, SIOCGIFFLAGS, &network);
 
1363
    if(ret == -1){
 
1364
      perror("ioctl SIOCGIFFLAGS");
 
1365
    } else if(network.ifr_flags & IFF_UP) {
 
1366
      network.ifr_flags &= ~IFF_UP; /* clear flag */
 
1367
      ret = ioctl(sd, SIOCSIFFLAGS, &network);
 
1368
      if(ret == -1){
 
1369
        perror("ioctl SIOCSIFFLAGS");
 
1370
      }
 
1371
    }
 
1372
    ret = (int)TEMP_FAILURE_RETRY(close(sd));
 
1373
    if(ret == -1){
 
1374
      perror("close");
 
1375
    }
 
1376
  }
 
1377
  
 
1378
  /* Removes the temp directory used by GPGME */
 
1379
  if(tempdir_created){
 
1380
    DIR *d;
 
1381
    struct dirent *direntry;
 
1382
    d = opendir(tempdir);
 
1383
    if(d == NULL){
 
1384
      if(errno != ENOENT){
 
1385
        perror("opendir");
 
1386
      }
 
1387
    } else {
 
1388
      while(true){
 
1389
        direntry = readdir(d);
 
1390
        if(direntry == NULL){
 
1391
          break;
 
1392
        }
 
1393
        /* Skip "." and ".." */
 
1394
        if(direntry->d_name[0] == '.'
 
1395
           and (direntry->d_name[1] == '\0'
 
1396
                or (direntry->d_name[1] == '.'
 
1397
                    and direntry->d_name[2] == '\0'))){
 
1398
          continue;
 
1399
        }
 
1400
        char *fullname = NULL;
 
1401
        ret = asprintf(&fullname, "%s/%s", tempdir,
 
1402
                       direntry->d_name);
 
1403
        if(ret < 0){
 
1404
          perror("asprintf");
 
1405
          continue;
 
1406
        }
 
1407
        ret = remove(fullname);
 
1408
        if(ret == -1){
 
1409
          fprintf(stderr, "remove(\"%s\"): %s\n", fullname,
 
1410
                  strerror(errno));
 
1411
        }
 
1412
        free(fullname);
 
1413
      }
 
1414
      closedir(d);
 
1415
    }
 
1416
    ret = rmdir(tempdir);
 
1417
    if(ret == -1 and errno != ENOENT){
 
1418
      perror("rmdir");
 
1419
    }
 
1420
  }
 
1421
  
 
1422
  return exitcode;
866
1423
}