/mandos/release

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

« back to all changes in this revision

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

  • Committer: teddy at bsnet
  • Date: 2010-08-11 20:06:18 UTC
  • mto: (237.2.182 mandos-local)
  • mto: This revision was merged to the branch mainline in revision 270.
  • Revision ID: teddy@fukt.bsnet.se-20100811200618-1uuv7p6cmrb2cmxu
* plugins.d/mandos-client.c: Added debug output.

Show diffs side-by-side

added added

removed removed

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