/mandos/release

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

« back to all changes in this revision

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

  • Committer: Björn Påhlsson
  • Date: 2010-05-18 16:56:54 UTC
  • mto: (24.1.154 mandos) (237.7.1 mandos)
  • mto: This revision was merged to the branch mainline in revision 270.
  • Revision ID: belorn@braxen-20100518165654-vanhudm9qzyn6c3s
half working on-demand password and approved code

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, 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_* */
41
96
#include <avahi-core/core.h>
42
97
#include <avahi-core/lookup.h>
43
98
#include <avahi-core/log.h>
45
100
#include <avahi-common/malloc.h>
46
101
#include <avahi-common/error.h>
47
102
 
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>
 
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_* */
68
119
 
69
120
#define BUFFER_SIZE 256
70
121
 
71
 
static int dh_bits = 1024;
72
 
 
73
 
static const char *keydir = "/conf/conf.d/mandos";
74
 
static const char *pubkeyfile = "pubkey.txt";
75
 
static const char *seckeyfile = "seckey.txt";
 
122
#define PATHDIR "/conf/conf.d/mandos"
 
123
#define SECKEY "seckey.txt"
 
124
#define PUBKEY "pubkey.txt"
76
125
 
77
126
bool debug = false;
 
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>";
78
130
 
79
 
/* Used for  */
 
131
/* Used for passing in values through the Avahi callback functions */
80
132
typedef struct {
81
 
  gnutls_session_t session;
 
133
  AvahiSimplePoll *simple_poll;
 
134
  AvahiServer *server;
82
135
  gnutls_certificate_credentials_t cred;
 
136
  unsigned int dh_bits;
83
137
  gnutls_dh_params_t dh_params;
84
 
} encrypted_session;
85
 
 
86
 
 
87
 
static ssize_t pgp_packet_decrypt (char *packet, size_t packet_size,
88
 
                                   char **new_packet,
89
 
                                   const char *homedir){
90
 
  gpgme_data_t dh_crypto, dh_plain;
 
138
  const char *priority;
91
139
  gpgme_ctx_t ctx;
 
140
} mandos_context;
 
141
 
 
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,
 
156
                  size_t buffer_capacity){
 
157
  if(buffer_length + BUFFER_SIZE > buffer_capacity){
 
158
    *buffer = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
 
159
    if(buffer == NULL){
 
160
      return 0;
 
161
    }
 
162
    buffer_capacity += BUFFER_SIZE;
 
163
  }
 
164
  return buffer_capacity;
 
165
}
 
166
 
 
167
/* 
 
168
 * Initialize GPGME.
 
169
 */
 
170
static bool init_gpgme(const char *seckey,
 
171
                       const char *pubkey, const char *tempdir){
92
172
  gpgme_error_t rc;
93
 
  ssize_t ret;
94
 
  ssize_t new_packet_capacity = 0;
95
 
  ssize_t new_packet_length = 0;
96
173
  gpgme_engine_info_t engine_info;
97
 
 
98
 
  if (debug){
99
 
    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");
100
214
  }
101
215
  
102
216
  /* Init GPGME */
103
217
  gpgme_check_version(NULL);
104
218
  rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
105
 
  if (rc != GPG_ERR_NO_ERROR){
 
219
  if(rc != GPG_ERR_NO_ERROR){
106
220
    fprintf(stderr, "bad gpgme_engine_check_version: %s: %s\n",
107
221
            gpgme_strsource(rc), gpgme_strerror(rc));
108
 
    return -1;
 
222
    return false;
109
223
  }
110
224
  
111
 
  /* Set GPGME home directory */
112
 
  rc = gpgme_get_engine_info (&engine_info);
113
 
  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){
114
228
    fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
115
229
            gpgme_strsource(rc), gpgme_strerror(rc));
116
 
    return -1;
 
230
    return false;
117
231
  }
118
232
  while(engine_info != NULL){
119
233
    if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
120
234
      gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
121
 
                            engine_info->file_name, homedir);
 
235
                            engine_info->file_name, tempdir);
122
236
      break;
123
237
    }
124
238
    engine_info = engine_info->next;
125
239
  }
126
240
  if(engine_info == NULL){
127
 
    fprintf(stderr, "Could not set home dir to %s\n", homedir);
128
 
    return -1;
129
 
  }
130
 
  
131
 
  /* Create new GPGME data buffer from packet buffer */
132
 
  rc = gpgme_data_new_from_mem(&dh_crypto, packet, packet_size, 0);
133
 
  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){
134
281
    fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
135
282
            gpgme_strsource(rc), gpgme_strerror(rc));
136
283
    return -1;
138
285
  
139
286
  /* Create new empty GPGME data buffer for the plaintext */
140
287
  rc = gpgme_data_new(&dh_plain);
141
 
  if (rc != GPG_ERR_NO_ERROR){
 
288
  if(rc != GPG_ERR_NO_ERROR){
142
289
    fprintf(stderr, "bad gpgme_data_new: %s: %s\n",
143
290
            gpgme_strsource(rc), gpgme_strerror(rc));
144
 
    return -1;
145
 
  }
146
 
  
147
 
  /* Create new GPGME "context" */
148
 
  rc = gpgme_new(&ctx);
149
 
  if (rc != GPG_ERR_NO_ERROR){
150
 
    fprintf(stderr, "bad gpgme_new: %s: %s\n",
151
 
            gpgme_strsource(rc), gpgme_strerror(rc));
152
 
    return -1;
153
 
  }
154
 
  
155
 
  /* Decrypt data from the FILE pointer to the plaintext data
156
 
     buffer */
157
 
  rc = gpgme_op_decrypt(ctx, dh_crypto, dh_plain);
158
 
  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){
159
299
    fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
160
300
            gpgme_strsource(rc), gpgme_strerror(rc));
161
 
    return -1;
162
 
  }
163
 
 
164
 
  if(debug){
165
 
    fprintf(stderr, "Decryption of OpenPGP packet succeeded\n");
166
 
  }
167
 
 
168
 
  if (debug){
169
 
    gpgme_decrypt_result_t result;
170
 
    result = gpgme_op_decrypt_result(ctx);
171
 
    if (result == NULL){
172
 
      fprintf(stderr, "gpgme_op_decrypt_result failed\n");
173
 
    } else {
174
 
      fprintf(stderr, "Unsupported algorithm: %s\n",
175
 
              result->unsupported_algorithm);
176
 
      fprintf(stderr, "Wrong key usage: %d\n",
177
 
              result->wrong_key_usage);
178
 
      if(result->file_name != NULL){
179
 
        fprintf(stderr, "File name: %s\n", result->file_name);
180
 
      }
181
 
      gpgme_recipient_t recipient;
182
 
      recipient = result->recipients;
183
 
      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;
184
317
        while(recipient != NULL){
185
318
          fprintf(stderr, "Public key algorithm: %s\n",
186
319
                  gpgme_pubkey_algo_name(recipient->pubkey_algo));
192
325
        }
193
326
      }
194
327
    }
 
328
    goto decrypt_end;
195
329
  }
196
330
  
197
 
  /* Delete the GPGME FILE pointer cryptotext data buffer */
198
 
  gpgme_data_release(dh_crypto);
 
331
  if(debug){
 
332
    fprintf(stderr, "Decryption of OpenPGP data succeeded\n");
 
333
  }
199
334
  
200
335
  /* Seek back to the beginning of the GPGME plaintext data buffer */
201
 
  if (gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET) == -1){
202
 
    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;
203
340
  }
204
341
  
205
 
  *new_packet = 0;
 
342
  *plaintext = NULL;
206
343
  while(true){
207
 
    if (new_packet_length + BUFFER_SIZE > new_packet_capacity){
208
 
      *new_packet = realloc(*new_packet,
209
 
                            (unsigned int)new_packet_capacity
210
 
                            + BUFFER_SIZE);
211
 
      if (*new_packet == NULL){
212
 
        perror("realloc");
213
 
        return -1;
214
 
      }
215
 
      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;
216
351
    }
217
352
    
218
 
    ret = gpgme_data_read(dh_plain, *new_packet + new_packet_length,
 
353
    ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
219
354
                          BUFFER_SIZE);
220
355
    /* Print the data, if any */
221
 
    if (ret == 0){
 
356
    if(ret == 0){
 
357
      /* EOF */
222
358
      break;
223
359
    }
224
360
    if(ret < 0){
225
361
      perror("gpgme_data_read");
226
 
      return -1;
227
 
    }
228
 
    new_packet_length += ret;
229
 
  }
230
 
 
231
 
  /* FIXME: check characters before printing to screen so to not print
232
 
     terminal control characters */
233
 
  /*   if(debug){ */
234
 
  /*     fprintf(stderr, "decrypted password is: "); */
235
 
  /*     fwrite(*new_packet, 1, new_packet_length, stderr); */
236
 
  /*     fprintf(stderr, "\n"); */
237
 
  /*   } */
 
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);
238
380
  
239
381
  /* Delete the GPGME plaintext data buffer */
240
382
  gpgme_data_release(dh_plain);
241
 
  return new_packet_length;
 
383
  return plaintext_length;
242
384
}
243
385
 
244
 
static const char * safer_gnutls_strerror (int value) {
245
 
  const char *ret = gnutls_strerror (value);
246
 
  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)
247
390
    ret = "(unknown)";
248
391
  return ret;
249
392
}
250
393
 
 
394
/* GnuTLS log function callback */
251
395
static void debuggnutls(__attribute__((unused)) int level,
252
396
                        const char* string){
253
 
  fprintf(stderr, "%s", string);
 
397
  fprintf(stderr, "GnuTLS: %s", string);
254
398
}
255
399
 
256
 
static int initgnutls(encrypted_session *es){
257
 
  const char *err;
 
400
static int init_gnutls_global(const char *pubkeyfilename,
 
401
                              const char *seckeyfilename){
258
402
  int ret;
259
403
  
260
404
  if(debug){
261
405
    fprintf(stderr, "Initializing GnuTLS\n");
262
406
  }
263
 
 
264
 
  if ((ret = gnutls_global_init ())
265
 
      != GNUTLS_E_SUCCESS) {
266
 
    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));
267
412
    return -1;
268
413
  }
269
 
 
270
 
  if (debug){
 
414
  
 
415
  if(debug){
 
416
    /* "Use a log level over 10 to enable all debugging options."
 
417
     * - GnuTLS manual
 
418
     */
271
419
    gnutls_global_set_log_level(11);
272
420
    gnutls_global_set_log_function(debuggnutls);
273
421
  }
274
422
  
275
 
  /* openpgp credentials */
276
 
  if ((ret = gnutls_certificate_allocate_credentials (&es->cred))
277
 
      != GNUTLS_E_SUCCESS) {
278
 
    fprintf (stderr, "memory error: %s\n",
279
 
             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();
280
432
    return -1;
281
433
  }
282
434
  
283
435
  if(debug){
284
 
    fprintf(stderr, "Attempting to use OpenPGP certificate %s"
285
 
            " and keyfile %s as GnuTLS credentials\n", pubkeyfile,
286
 
            seckeyfile);
 
436
    fprintf(stderr, "Attempting to use OpenPGP public key %s and"
 
437
            " secret key %s as GnuTLS credentials\n", pubkeyfilename,
 
438
            seckeyfilename);
287
439
  }
288
440
  
289
441
  ret = gnutls_certificate_set_openpgp_key_file
290
 
    (es->cred, pubkeyfile, seckeyfile, GNUTLS_OPENPGP_FMT_BASE64);
291
 
  if (ret != GNUTLS_E_SUCCESS) {
292
 
    fprintf
293
 
      (stderr, "Error[%d] while reading the OpenPGP key pair ('%s',"
294
 
       " '%s')\n",
295
 
       ret, pubkeyfile, seckeyfile);
296
 
    fprintf(stdout, "The Error is: %s\n",
297
 
            safer_gnutls_strerror(ret));
298
 
    return -1;
299
 
  }
300
 
  
301
 
  //GnuTLS server initialization
302
 
  if ((ret = gnutls_dh_params_init (&es->dh_params))
303
 
      != GNUTLS_E_SUCCESS) {
304
 
    fprintf (stderr, "Error in dh parameter initialization: %s\n",
305
 
             safer_gnutls_strerror(ret));
306
 
    return -1;
307
 
  }
308
 
  
309
 
  if ((ret = gnutls_dh_params_generate2 (es->dh_params, dh_bits))
310
 
      != GNUTLS_E_SUCCESS) {
311
 
    fprintf (stderr, "Error in prime generation: %s\n",
312
 
             safer_gnutls_strerror(ret));
313
 
    return -1;
314
 
  }
315
 
  
316
 
  gnutls_certificate_set_dh_params (es->cred, es->dh_params);
317
 
  
318
 
  // GnuTLS session creation
319
 
  if ((ret = gnutls_init (&es->session, GNUTLS_SERVER))
320
 
      != 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){
321
489
    fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
322
490
            safer_gnutls_strerror(ret));
323
491
  }
324
492
  
325
 
  if ((ret = gnutls_priority_set_direct (es->session, "NORMAL", &err))
326
 
      != GNUTLS_E_SUCCESS) {
327
 
    fprintf(stderr, "Syntax error at: %s\n", err);
328
 
    fprintf(stderr, "GnuTLS error: %s\n",
329
 
            safer_gnutls_strerror(ret));
330
 
    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
    }
331
509
  }
332
510
  
333
 
  if ((ret = gnutls_credentials_set
334
 
       (es->session, GNUTLS_CRD_CERTIFICATE, es->cred))
335
 
      != GNUTLS_E_SUCCESS) {
336
 
    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",
337
521
            safer_gnutls_strerror(ret));
 
522
    gnutls_deinit(*session);
338
523
    return -1;
339
524
  }
340
525
  
341
526
  /* ignore client certificate if any. */
342
 
  gnutls_certificate_server_set_request (es->session,
343
 
                                         GNUTLS_CERT_IGNORE);
 
527
  gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE);
344
528
  
345
 
  gnutls_dh_set_prime_bits (es->session, dh_bits);
 
529
  gnutls_dh_set_prime_bits(*session, mc.dh_bits);
346
530
  
347
531
  return 0;
348
532
}
349
533
 
 
534
/* Avahi log function callback */
350
535
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
351
536
                      __attribute__((unused)) const char *txt){}
352
537
 
 
538
/* Called when a Mandos server is found */
353
539
static int start_mandos_communication(const char *ip, uint16_t port,
354
 
                                      AvahiIfIndex if_index){
355
 
  int ret, tcp_sd;
356
 
  struct sockaddr_in6 to;
357
 
  encrypted_session es;
 
540
                                      AvahiIfIndex if_index,
 
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;
358
548
  char *buffer = NULL;
359
 
  char *decrypted_buffer;
 
549
  char *decrypted_buffer = NULL;
360
550
  size_t buffer_length = 0;
361
551
  size_t buffer_capacity = 0;
362
 
  ssize_t decrypted_buffer_size;
363
 
  size_t written = 0;
364
 
  int retval = 0;
365
 
  char interface[IF_NAMESIZE];
 
552
  size_t written;
 
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
  }
366
581
  
367
582
  if(debug){
368
 
    fprintf(stderr, "Setting up a tcp connection to %s, port %d\n",
369
 
            ip, port);
 
583
    fprintf(stderr, "Setting up a TCP connection to %s, port %" PRIu16
 
584
            "\n", ip, port);
370
585
  }
371
586
  
372
 
  tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
373
 
  if(tcp_sd < 0) {
 
587
  tcp_sd = socket(pf, SOCK_STREAM, 0);
 
588
  if(tcp_sd < 0){
 
589
    int e = errno;
374
590
    perror("socket");
375
 
    return -1;
376
 
  }
377
 
  
378
 
  if(if_indextoname((unsigned int)if_index, interface) == NULL){
379
 
    if(debug){
380
 
      perror("if_indextoname");
381
 
    }
382
 
    return -1;
383
 
  }
384
 
  
385
 
  if(debug){
386
 
    fprintf(stderr, "Binding to interface %s\n", interface);
387
 
  }
388
 
  
389
 
  memset(&to,0,sizeof(to));     /* Spurious warning */
390
 
  to.sin6_family = AF_INET6;
391
 
  ret = inet_pton(AF_INET6, ip, &to.sin6_addr);
392
 
  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;
393
610
    perror("inet_pton");
394
 
    return -1;
395
 
  }  
 
611
    errno = e;
 
612
    goto mandos_end;
 
613
  }
396
614
  if(ret == 0){
 
615
    int e = errno;
397
616
    fprintf(stderr, "Bad address: %s\n", ip);
398
 
    return -1;
399
 
  }
400
 
  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
  }
401
642
  
402
 
  to.sin6_scope_id = (uint32_t)if_index;
 
643
  if(quit_now){
 
644
    errno = EINTR;
 
645
    goto mandos_end;
 
646
  }
403
647
  
404
648
  if(debug){
405
 
    fprintf(stderr, "Connection to: %s, port %d\n", ip, port);
406
 
    char addrstr[INET6_ADDRSTRLEN] = "";
407
 
    if(inet_ntop(to.sin6_family, &(to.sin6_addr), addrstr,
408
 
                 sizeof(addrstr)) == NULL){
 
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){
409
672
      perror("inet_ntop");
410
673
    } else {
411
674
      if(strcmp(addrstr, ip) != 0){
412
 
        fprintf(stderr, "Canonical address form: %s\n",
413
 
                addrstr, ntohs(to.sin6_port));
 
675
        fprintf(stderr, "Canonical address form: %s\n", addrstr);
414
676
      }
415
677
    }
416
678
  }
417
679
  
418
 
  ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
419
 
  if (ret < 0){
 
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;
420
692
    perror("connect");
421
 
    return -1;
422
 
  }
423
 
  
424
 
  ret = initgnutls (&es);
425
 
  if (ret != 0){
426
 
    retval = -1;
427
 
    return -1;
428
 
  }
429
 
  
430
 
  gnutls_transport_set_ptr (es.session,
431
 
                            (gnutls_transport_ptr_t) tcp_sd);
 
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;
 
703
  written = 0;
 
704
  while(true){
 
705
    size_t out_size = strlen(out);
 
706
    ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
 
707
                                   out_size - written));
 
708
    if(ret == -1){
 
709
      int e = errno;
 
710
      perror("write");
 
711
      errno = e;
 
712
      goto mandos_end;
 
713
    }
 
714
    written += (size_t)ret;
 
715
    if(written < out_size){
 
716
      continue;
 
717
    } else {
 
718
      if(out == mandos_protocol_version){
 
719
        written = 0;
 
720
        out = "\r\n";
 
721
      } else {
 
722
        break;
 
723
      }
 
724
    }
 
725
  
 
726
    if(quit_now){
 
727
      errno = EINTR;
 
728
      goto mandos_end;
 
729
    }
 
730
  }
432
731
  
433
732
  if(debug){
434
733
    fprintf(stderr, "Establishing TLS session with %s\n", ip);
435
734
  }
436
735
  
437
 
  ret = gnutls_handshake (es.session);
438
 
  
439
 
  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){
440
757
    if(debug){
441
 
      fprintf(stderr, "\n*** Handshake failed ***\n");
442
 
      gnutls_perror (ret);
 
758
      fprintf(stderr, "*** GnuTLS Handshake failed ***\n");
 
759
      gnutls_perror(ret);
443
760
    }
444
 
    retval = -1;
445
 
    goto exit;
 
761
    errno = EPROTO;
 
762
    goto mandos_end;
446
763
  }
447
764
  
448
 
  //Retrieve OpenPGP packet that contains the wanted password
 
765
  /* Read OpenPGP packet that contains the wanted password */
449
766
  
450
767
  if(debug){
451
 
    fprintf(stderr, "Retrieving pgp encrypted password from %s\n",
 
768
    fprintf(stderr, "Retrieving OpenPGP encrypted password from %s\n",
452
769
            ip);
453
770
  }
454
 
 
 
771
  
455
772
  while(true){
456
 
    if (buffer_length + BUFFER_SIZE > buffer_capacity){
457
 
      buffer = realloc(buffer, buffer_capacity + BUFFER_SIZE);
458
 
      if (buffer == NULL){
459
 
        perror("realloc");
460
 
        goto exit;
461
 
      }
462
 
      buffer_capacity += BUFFER_SIZE;
463
 
    }
464
 
    
465
 
    ret = gnutls_record_recv
466
 
      (es.session, buffer+buffer_length, BUFFER_SIZE);
467
 
    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){
468
796
      break;
469
797
    }
470
 
    if (ret < 0){
471
 
      switch(ret){
 
798
    if(sret < 0){
 
799
      switch(sret){
472
800
      case GNUTLS_E_INTERRUPTED:
473
801
      case GNUTLS_E_AGAIN:
474
802
        break;
475
803
      case GNUTLS_E_REHANDSHAKE:
476
 
        ret = gnutls_handshake (es.session);
477
 
        if (ret < 0){
478
 
          fprintf(stderr, "\n*** Handshake failed ***\n");
479
 
          gnutls_perror (ret);
480
 
          retval = -1;
481
 
          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;
482
817
        }
483
818
        break;
484
819
      default:
485
820
        fprintf(stderr, "Unknown error while reading data from"
486
 
                " encrypted session with mandos server\n");
487
 
        retval = -1;
488
 
        gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
489
 
        goto exit;
 
821
                " encrypted session with Mandos server\n");
 
822
        gnutls_bye(session, GNUTLS_SHUT_RDWR);
 
823
        errno = EIO;
 
824
        goto mandos_end;
490
825
      }
491
826
    } else {
492
 
      buffer_length += (size_t) ret;
493
 
    }
494
 
  }
495
 
  
496
 
  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;
497
850
    decrypted_buffer_size = pgp_packet_decrypt(buffer,
498
851
                                               buffer_length,
499
 
                                               &decrypted_buffer,
500
 
                                               keydir);
501
 
    if (decrypted_buffer_size >= 0){
 
852
                                               &decrypted_buffer);
 
853
    if(decrypted_buffer_size >= 0){
 
854
      
 
855
      written = 0;
502
856
      while(written < (size_t) decrypted_buffer_size){
503
 
        ret = (int)fwrite (decrypted_buffer + written, 1,
504
 
                           (size_t)decrypted_buffer_size - written,
505
 
                           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);
506
865
        if(ret == 0 and ferror(stdout)){
 
866
          int e = errno;
507
867
          if(debug){
508
868
            fprintf(stderr, "Error writing encrypted data: %s\n",
509
869
                    strerror(errno));
510
870
          }
511
 
          retval = -1;
512
 
          break;
 
871
          errno = e;
 
872
          goto mandos_end;
513
873
        }
514
874
        written += (size_t)ret;
515
875
      }
516
 
      free(decrypted_buffer);
517
 
    } 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;
518
899
      retval = -1;
519
900
    }
520
 
  }
521
 
 
522
 
  //shutdown procedure
523
 
 
524
 
  if(debug){
525
 
    fprintf(stderr, "Closing TLS session\n");
526
 
  }
527
 
 
528
 
  free(buffer);
529
 
  gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
530
 
 exit:
531
 
  close(tcp_sd);
532
 
  gnutls_deinit (es.session);
533
 
  gnutls_certificate_free_credentials (es.cred);
534
 
  gnutls_global_deinit ();
 
901
    errno = e;
 
902
  }
535
903
  return retval;
536
904
}
537
905
 
538
 
static AvahiSimplePoll *simple_poll = NULL;
539
 
static AvahiServer *server = NULL;
540
 
 
541
 
static void resolve_callback(
542
 
    AvahiSServiceResolver *r,
543
 
    AvahiIfIndex interface,
544
 
    AVAHI_GCC_UNUSED AvahiProtocol protocol,
545
 
    AvahiResolverEvent event,
546
 
    const char *name,
547
 
    const char *type,
548
 
    const char *domain,
549
 
    const char *host_name,
550
 
    const AvahiAddress *address,
551
 
    uint16_t port,
552
 
    AVAHI_GCC_UNUSED AvahiStringList *txt,
553
 
    AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
554
 
    AVAHI_GCC_UNUSED void* userdata) {
555
 
    
556
 
  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);
557
921
  
558
922
  /* Called whenever a service has been resolved successfully or
559
923
     timed out */
560
924
  
561
 
  switch (event) {
 
925
  if(quit_now){
 
926
    return;
 
927
  }
 
928
  
 
929
  switch(event){
562
930
  default:
563
931
  case AVAHI_RESOLVER_FAILURE:
564
 
    fprintf(stderr, "(Resolver) Failed to resolve service '%s' of"
565
 
            " type '%s' in domain '%s': %s\n", name, type, domain,
566
 
            avahi_strerror(avahi_server_errno(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)));
567
935
    break;
568
936
    
569
937
  case AVAHI_RESOLVER_FOUND:
571
939
      char ip[AVAHI_ADDRESS_STR_MAX];
572
940
      avahi_address_snprint(ip, sizeof(ip), address);
573
941
      if(debug){
574
 
        fprintf(stderr, "Mandos server \"%s\" found on %s (%s) on"
575
 
                " 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);
576
945
      }
577
 
      int ret = start_mandos_communication(ip, port, interface);
578
 
      if (ret == 0){
579
 
        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);
580
950
      }
581
951
    }
582
952
  }
583
953
  avahi_s_service_resolver_free(r);
584
954
}
585
955
 
586
 
static void browse_callback(
587
 
    AvahiSServiceBrowser *b,
588
 
    AvahiIfIndex interface,
589
 
    AvahiProtocol protocol,
590
 
    AvahiBrowserEvent event,
591
 
    const char *name,
592
 
    const char *type,
593
 
    const char *domain,
594
 
    AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
595
 
    void* userdata) {
596
 
    
597
 
    AvahiServer *s = userdata;
598
 
    assert(b);                  /* Spurious warning */
599
 
    
600
 
    /* Called whenever a new services becomes available on the LAN or
601
 
       is removed from the LAN */
602
 
    
603
 
    switch (event) {
 
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);
 
967
  
 
968
  /* Called whenever a new services becomes available on the LAN or
 
969
     is removed from the LAN */
 
970
  
 
971
  if(quit_now){
 
972
    return;
 
973
  }
 
974
  
 
975
  switch(event){
 
976
  default:
 
977
  case AVAHI_BROWSER_FAILURE:
 
978
    
 
979
    fprintf(stderr, "(Avahi browser) %s\n",
 
980
            avahi_strerror(avahi_server_errno(mc.server)));
 
981
    avahi_simple_poll_quit(mc.simple_poll);
 
982
    return;
 
983
    
 
984
  case AVAHI_BROWSER_NEW:
 
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)));
 
995
    break;
 
996
    
 
997
  case AVAHI_BROWSER_REMOVE:
 
998
    break;
 
999
    
 
1000
  case AVAHI_BROWSER_ALL_FOR_NOW:
 
1001
  case AVAHI_BROWSER_CACHE_EXHAUSTED:
 
1002
    if(debug){
 
1003
      fprintf(stderr, "No Mandos server found, still searching...\n");
 
1004
    }
 
1005
    break;
 
1006
  }
 
1007
}
 
1008
 
 
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;
 
1021
}
 
1022
 
 
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
    };
 
1116
    
 
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);
 
1168
        break;
 
1169
      default:
 
1170
        return ARGP_ERR_UNKNOWN;
 
1171
      }
 
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:
604
1185
    default:
605
 
    case AVAHI_BROWSER_FAILURE:
606
 
      
607
 
      fprintf(stderr, "(Browser) %s\n",
608
 
              avahi_strerror(avahi_server_errno(server)));
609
 
      avahi_simple_poll_quit(simple_poll);
610
 
      return;
611
 
      
612
 
    case AVAHI_BROWSER_NEW:
613
 
      /* We ignore the returned resolver object. In the callback
614
 
         function we free it. If the server is terminated before
615
 
         the callback function is called the server will free
616
 
         the resolver for us. */
617
 
      
618
 
      if (!(avahi_s_service_resolver_new(s, interface, protocol, name,
619
 
                                         type, domain,
620
 
                                         AVAHI_PROTO_INET6, 0,
621
 
                                         resolve_callback, s)))
622
 
        fprintf(stderr, "Failed to resolve service '%s': %s\n", name,
623
 
                avahi_strerror(avahi_server_errno(s)));
624
 
      break;
625
 
      
626
 
    case AVAHI_BROWSER_REMOVE:
627
 
      break;
628
 
      
629
 
    case AVAHI_BROWSER_ALL_FOR_NOW:
630
 
    case AVAHI_BROWSER_CACHE_EXHAUSTED:
631
 
      break;
632
 
    }
633
 
}
634
 
 
635
 
/* Combines file name and path and returns the malloced new
636
 
   string. some sane checks could/should be added */
637
 
static const char *combinepath(const char *first, const char *second){
638
 
  size_t f_len = strlen(first);
639
 
  size_t s_len = strlen(second);
640
 
  char *tmp = malloc(f_len + s_len + 2);
641
 
  if (tmp == NULL){
642
 
    return NULL;
643
 
  }
644
 
  if(f_len > 0){
645
 
    memcpy(tmp, first, f_len);
646
 
  }
647
 
  tmp[f_len] = '/';
648
 
  if(s_len > 0){
649
 
    memcpy(tmp + f_len + 1, second, s_len);
650
 
  }
651
 
  tmp[f_len + 1 + s_len] = '\0';
652
 
  return tmp;
653
 
}
654
 
 
655
 
 
656
 
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
 
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'){
 
1276
    if_index = (AvahiIfIndex) if_nametoindex(interface);
 
1277
    if(if_index == 0){
 
1278
      fprintf(stderr, "No such interface: \"%s\"\n", interface);
 
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__ */
 
1304
    
 
1305
    sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
 
1306
    if(sd < 0){
 
1307
      perror("socket");
 
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;
 
1324
    }
 
1325
    strcpy(network.ifr_name, interface);
 
1326
    ret = ioctl(sd, SIOCGIFFLAGS, &network);
 
1327
    if(ret == -1){
 
1328
      perror("ioctl SIOCGIFFLAGS");
 
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;
 
1345
    }
 
1346
    if((network.ifr_flags & IFF_UP) == 0){
 
1347
      network.ifr_flags |= IFF_UP;
 
1348
      take_down_interface = true;
 
1349
      ret = ioctl(sd, SIOCSIFFLAGS, &network);
 
1350
      if(ret == -1){
 
1351
        take_down_interface = false;
 
1352
        perror("ioctl SIOCSIFFLAGS");
 
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
  {
657
1533
    AvahiServerConfig config;
658
 
    AvahiSServiceBrowser *sb = NULL;
659
 
    int error;
660
 
    int ret;
661
 
    int debug_int = 0;
662
 
    int returncode = EXIT_SUCCESS;
663
 
    const char *interface = NULL;
664
 
    AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
665
 
    char *connect_to = NULL;
666
 
    
667
 
    debug_int = debug ? 1 : 0;
668
 
    while (true){
669
 
      static struct option long_options[] = {
670
 
        {"debug", no_argument, &debug_int, 1},
671
 
        {"connect", required_argument, NULL, 'C'},
672
 
        {"interface", required_argument, NULL, 'i'},
673
 
        {"keydir", required_argument, NULL, 'd'},
674
 
        {"seckey", required_argument, NULL, 'c'},
675
 
        {"pubkey", required_argument, NULL, 'k'},
676
 
        {"dh-bits", required_argument, NULL, 'D'},
677
 
        {0, 0, 0, 0} };
678
 
      
679
 
      int option_index = 0;
680
 
      ret = getopt_long (argc, argv, "i:", long_options,
681
 
                         &option_index);
682
 
      
683
 
      if (ret == -1){
684
 
        break;
685
 
      }
686
 
      
687
 
      switch(ret){
688
 
      case 0:
689
 
        break;
690
 
      case 'i':
691
 
        interface = optarg;
692
 
        break;
693
 
      case 'C':
694
 
        connect_to = optarg;
695
 
        break;
696
 
      case 'd':
697
 
        keydir = optarg;
698
 
        break;
699
 
      case 'c':
700
 
        pubkeyfile = optarg;
701
 
        break;
702
 
      case 'k':
703
 
        seckeyfile = optarg;
704
 
        break;
705
 
      case 'D':
706
 
        dh_bits = atoi(optarg);
707
 
        break;
708
 
      case '?':
709
 
        break
710
 
      default:
711
 
        exit(EXIT_FAILURE);
712
 
      }
713
 
    }
714
 
    debug = debug_int ? true : false;
715
 
    
716
 
    pubkeyfile = combinepath(keydir, pubkeyfile);
717
 
    if (pubkeyfile == NULL){
718
 
      perror("combinepath");
719
 
      goto exit;
720
 
    }
721
 
    
722
 
    if(interface != NULL){
723
 
      if_index = (AvahiIfIndex) if_nametoindex(interface);
724
 
      if(if_index == 0){
725
 
        fprintf(stderr, "No such interface: \"%s\"\n", interface);
726
 
        exit(EXIT_FAILURE);
727
 
      }
728
 
    }
729
 
    
730
 
    if(connect_to != NULL){
731
 
      /* Connect directly, do not use Zeroconf */
732
 
      /* (Mainly meant for debugging) */
733
 
      char *address = strrchr(connect_to, ':');
734
 
      if(address == NULL){
735
 
        fprintf(stderr, "No colon in address\n");
736
 
        exit(EXIT_FAILURE);
737
 
      }
738
 
      errno = 0;
739
 
      uint16_t port = (uint16_t) strtol(address+1, NULL, 10);
740
 
      if(errno){
741
 
        perror("Bad port number");
742
 
        exit(EXIT_FAILURE);
743
 
      }
744
 
      *address = '\0';
745
 
      address = connect_to;
746
 
      ret = start_mandos_communication(address, port, if_index);
747
 
      if(ret < 0){
748
 
        exit(EXIT_FAILURE);
749
 
      } else {
750
 
        exit(EXIT_SUCCESS);
751
 
      }
752
 
    }
753
 
    
754
 
    seckeyfile = combinepath(keydir, seckeyfile);
755
 
    if (seckeyfile == NULL){
756
 
      perror("combinepath");
757
 
      goto exit;
758
 
    }
759
 
    
760
 
    if (not debug){
761
 
      avahi_set_log_function(empty_log);
762
 
    }
763
 
    
764
 
    /* Initialize the psuedo-RNG */
765
 
    srand((unsigned int) time(NULL));
766
 
 
767
 
    /* Allocate main loop object */
768
 
    if (!(simple_poll = avahi_simple_poll_new())) {
769
 
        fprintf(stderr, "Failed to create simple poll object.\n");
770
 
        
771
 
        goto exit;
772
 
    }
773
 
 
774
 
    /* Do not publish any local records */
 
1534
    /* Do not publish any local Zeroconf records */
775
1535
    avahi_server_config_init(&config);
776
1536
    config.publish_hinfo = 0;
777
1537
    config.publish_addresses = 0;
778
1538
    config.publish_workstation = 0;
779
1539
    config.publish_domain = 0;
780
 
 
 
1540
    
781
1541
    /* Allocate a new server */
782
 
    server = avahi_server_new(avahi_simple_poll_get(simple_poll),
783
 
                              &config, NULL, NULL, &error);
784
 
 
785
 
    /* 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 */
786
1547
    avahi_server_config_free(&config);
787
 
 
788
 
    /* Check if creating the server object succeeded */
789
 
    if (!server) {
790
 
        fprintf(stderr, "Failed to create server: %s\n",
791
 
                avahi_strerror(error));
792
 
        returncode = EXIT_FAILURE;
793
 
        goto exit;
794
 
    }
795
 
    
796
 
    /* Create the service browser */
797
 
    sb = avahi_s_service_browser_new(server, if_index,
798
 
                                     AVAHI_PROTO_INET6,
799
 
                                     "_mandos._tcp", NULL, 0,
800
 
                                     browse_callback, server);
801
 
    if (!sb) {
802
 
        fprintf(stderr, "Failed to create service browser: %s\n",
803
 
                avahi_strerror(avahi_server_errno(server)));
804
 
        returncode = EXIT_FAILURE;
805
 
        goto exit;
806
 
    }
807
 
    
808
 
    /* Run the main loop */
809
 
 
810
 
    if (debug){
811
 
      fprintf(stderr, "Starting avahi loop search\n");
812
 
    }
813
 
    
814
 
    avahi_simple_poll_loop(simple_poll);
815
 
    
816
 
 exit:
817
 
 
818
 
    if (debug){
819
 
      fprintf(stderr, "%s exiting\n", argv[0]);
820
 
    }
821
 
    
822
 
    /* Cleanup things */
823
 
    if (sb)
824
 
        avahi_s_service_browser_free(sb);
825
 
    
826
 
    if (server)
827
 
        avahi_server_free(server);
828
 
 
829
 
    if (simple_poll)
830
 
        avahi_simple_poll_free(simple_poll);
831
 
    free(pubkeyfile);
832
 
    free(seckeyfile);
833
 
    
834
 
    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;
835
1707
}