/mandos/trunk

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

« back to all changes in this revision

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

  • Committer: Teddy Hogeborn
  • Date: 2011-07-13 01:11:12 UTC
  • Revision ID: teddy@fukt.bsnet.se-20110713011112-bgf3tenqq60uoyyb
* Makefile (plugins.d/mandos-client): Bug fix: Put $^ before all
                                      libraries.  Remove "$(COMMON)".
                                      Thanks to Angel Abad
                                      <angelabad@ubuntu.com>.
* initramfs-tools-script: Work around change in initramfs-tools,
                          Debian bug #633582.
* plugins.d/mandos-client.c (init_gnutls_global): Bug fix: check for
                errors from gnutls_certificate_allocate_credentials().

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