/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: 2010-06-19 00:37:04 UTC
  • mto: (24.1.149 mandos)
  • mto: This revision was merged to the branch mainline in revision 417.
  • Revision ID: teddy@fukt.bsnet.se-20100619003704-vpicvssvv1ktg2om
* mandos (ClientHandler.handle): Set up the GnuTLS session object
                                 before reading the protocol number.
 (ClientHandler.handle/ProxyObject): New.

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