/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-16 23:28:39 UTC
  • Revision ID: teddy@fukt.bsnet.se-20090916232839-3o7i8qmcdcz5j1ya
* init.d-mandos (Required-Start, Required-Stop): Bug fix: Added
                 "$syslog", thanks to Petter Reinholdtsen
                 <pere@hungry.com> (Debian bug #546928).

* initramfs-tools-script: Removed erroneous comment.

* plugins.d/askpass-fifo.c: Removed TEMP_FAILURE_RETRY since it is
                            not needed.

* plugins.d/mandos-client.c (main): Bug fix: Initialize
                                    "old_sigterm_action".

* plugins.d/splashy.c (main): Bug fix: really check return value from
                              "sigaddset".  Fix some warnings on
                              64-bit systems.

* plugins.d/usplash.c (termination_handler, main): Save received
                                                   signal and
                                                   re-raise it on
                                                   exit.
  (usplash_write): Do not close FIFO, instead, take an additional file
                   descriptor pointer to it and open only when needed
                   (all callers changed).  Abort immediately on EINTR.
                   Bug fix:  Add NUL byte on single-word commands.
                   Ignore "interrupted_by_signal".
  (makeprompt, find_usplash): New; broken out from "main()".
  (find_usplash): Bug fix: close /proc/<pid>/cmdline FD on error.
  (main): Reorganized to jump to a new "failure" label on any error.
          Bug fix: check return values from sigaddset.
          New variable "usplash_accessed" to flag if usplash(8) needs
          to be killed and restarted.  Removed the "an_error_occured"
          variable.

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