/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-09-08 06:28:20 UTC
  • Revision ID: teddy@fukt.bsnet.se-20090908062820-xgkdkrnvuze9vc72
* debian/mandos-client.README.Debian: Improved wording and formatting.
                                      Updated location of nfsroot.txt.
* debian/mandos.README.Debian: Improved wording and formatting.

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