/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: Björn Påhlsson
  • Date: 2011-06-19 20:25:38 UTC
  • mto: This revision was merged to the branch mainline in revision 485.
  • Revision ID: belorn@fukt.bsnet.se-20110619202538-0js072v8fso12u07
prepended mandos plugin to error messages in each plugin. Added a better way in TODO.

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