/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

* Makefile (DOCBOOKTOMAN, DOCBOOKTOHTML, common.ent, mandos,
            mandos-keygen, mandos-ctl, mandos.lsm): Add $(strip) to
                                                    make commands
                                                    easier to read
                                                    when run.
           (plugins.d/mandos-client): Use $(LINK.c) to compile in the
                                      same way as the other programs -
                                      i.e. not via an object file.

Show diffs side-by-side

added added

removed removed

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