/mandos/trunk

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

« back to all changes in this revision

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

  • Committer: Teddy Hogeborn
  • Date: 2010-09-12 03:00:40 UTC
  • Revision ID: teddy@fukt.bsnet.se-20100912030040-b0uopyennste9fdh
Documentation changes:

* DBUS-API: New file documenting the server D-Bus interface.

* clients.conf: Add examples of new approval settings.

* debian/mandos.docs: Added "DBUS-API".

* mandos-clients.conf.xml (OPTIONS): Added "approved_by_default",
                                     "approval_delay", and
                                     "approval_duration".
* mandos.xml (D-BUS INTERFACE): Refer to the "DBUS-API" file.
  (BUGS): Remove mention of lack of a remote query interface.

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