/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/mandosclient.c

  • Committer: Teddy Hogeborn
  • Date: 2008-08-03 03:33:56 UTC
  • Revision ID: teddy@fukt.bsnet.se-20080803033356-6aemgj0g0hoz91ow
* plugins.d/mandosclient.c (pgp_packet_decrypt): Renamed variables.
                                                 On debug, show
                                                 decrypted plaintext
                                                 in hexadecimal.  Free
                                                 the GPGME data
                                                 buffers even on
                                                 errors.

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 © 2008-2011 Teddy Hogeborn
13
 
 * Copyright © 2008-2011 Björn Påhlsson
 
12
 * Copyright © 2007-2008 Teddy Hogeborn & Björn Påhlsson
14
13
 * 
15
14
 * This program is free software: you can redistribute it and/or
16
15
 * modify it under the terms of the GNU General Public License as
26
25
 * along with this program.  If not, see
27
26
 * <http://www.gnu.org/licenses/>.
28
27
 * 
29
 
 * Contact the authors at <mandos@recompile.se>.
 
28
 * Contact the authors at <mandos@fukt.bsnet.se>.
30
29
 */
31
30
 
32
31
/* Needed by GPGME, specifically gpgme_data_seek() */
33
 
#ifndef _LARGEFILE_SOURCE
34
32
#define _LARGEFILE_SOURCE
35
 
#endif
36
 
#ifndef _FILE_OFFSET_BITS
37
33
#define _FILE_OFFSET_BITS 64
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(), S_ISREG */
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
 
                                   program_invocation_short_name */
67
 
#include <time.h>               /* nanosleep(), time(), sleep() */
 
34
 
 
35
#include <stdio.h>
 
36
#include <assert.h>
 
37
#include <stdlib.h>
 
38
#include <time.h>
 
39
#include <net/if.h>             /* if_nametoindex */
 
40
#include <sys/ioctl.h>          /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
 
41
                                   SIOCSIFFLAGS */
68
42
#include <net/if.h>             /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
69
 
                                   SIOCSIFFLAGS, if_indextoname(),
70
 
                                   if_nametoindex(), IF_NAMESIZE */
71
 
#include <netinet/in.h>         /* IN6_IS_ADDR_LINKLOCAL,
72
 
                                   INET_ADDRSTRLEN, INET6_ADDRSTRLEN
73
 
                                */
74
 
#include <unistd.h>             /* close(), SEEK_SET, off_t, write(),
75
 
                                   getuid(), getgid(), seteuid(),
76
 
                                   setgid(), pause() */
77
 
#include <arpa/inet.h>          /* inet_pton(), htons, inet_ntop() */
78
 
#include <iso646.h>             /* not, or, and */
79
 
#include <argp.h>               /* struct argp_option, error_t, struct
80
 
                                   argp_state, struct argp,
81
 
                                   argp_parse(), ARGP_KEY_ARG,
82
 
                                   ARGP_KEY_END, ARGP_ERR_UNKNOWN */
83
 
#include <signal.h>             /* sigemptyset(), sigaddset(),
84
 
                                   sigaction(), SIGTERM, sig_atomic_t,
85
 
                                   raise() */
86
 
#include <sysexits.h>           /* EX_OSERR, EX_USAGE, EX_UNAVAILABLE,
87
 
                                   EX_NOHOST, EX_IOERR, EX_PROTOCOL */
88
 
#include <sys/wait.h>           /* waitpid(), WIFEXITED(),
89
 
                                   WEXITSTATUS(), WTERMSIG() */
90
 
 
91
 
#ifdef __linux__
92
 
#include <sys/klog.h>           /* klogctl() */
93
 
#endif  /* __linux__ */
94
 
 
95
 
/* Avahi */
96
 
/* All Avahi types, constants and functions
97
 
 Avahi*, avahi_*,
98
 
 AVAHI_* */
 
43
                                   SIOCSIFFLAGS */
 
44
 
99
45
#include <avahi-core/core.h>
100
46
#include <avahi-core/lookup.h>
101
47
#include <avahi-core/log.h>
103
49
#include <avahi-common/malloc.h>
104
50
#include <avahi-common/error.h>
105
51
 
106
 
/* GnuTLS */
107
 
#include <gnutls/gnutls.h>      /* All GnuTLS types, constants and
108
 
                                   functions:
109
 
                                   gnutls_*
110
 
                                   init_gnutls_session(),
111
 
                                   GNUTLS_* */
112
 
#include <gnutls/openpgp.h>
113
 
                          /* gnutls_certificate_set_openpgp_key_file(),
114
 
                                   GNUTLS_OPENPGP_FMT_BASE64 */
115
 
 
116
 
/* GPGME */
117
 
#include <gpgme.h>              /* All GPGME types, constants and
118
 
                                   functions:
119
 
                                   gpgme_*
120
 
                                   GPGME_PROTOCOL_OpenPGP,
121
 
                                   GPG_ERR_NO_* */
 
52
//mandos client part
 
53
#include <sys/types.h>          /* socket(), inet_pton() */
 
54
#include <sys/socket.h>         /* socket(), struct sockaddr_in6,
 
55
                                   struct in6_addr, inet_pton() */
 
56
#include <gnutls/gnutls.h>      /* All GnuTLS stuff */
 
57
#include <gnutls/openpgp.h>     /* GnuTLS with openpgp stuff */
 
58
 
 
59
#include <unistd.h>             /* close() */
 
60
#include <netinet/in.h>
 
61
#include <stdbool.h>            /* true */
 
62
#include <string.h>             /* memset */
 
63
#include <arpa/inet.h>          /* inet_pton() */
 
64
#include <iso646.h>             /* not */
 
65
 
 
66
// gpgme
 
67
#include <errno.h>              /* perror() */
 
68
#include <gpgme.h>
 
69
 
 
70
// getopt_long
 
71
#include <getopt.h>
122
72
 
123
73
#define BUFFER_SIZE 256
124
74
 
125
 
#define PATHDIR "/conf/conf.d/mandos"
126
 
#define SECKEY "seckey.txt"
127
 
#define PUBKEY "pubkey.txt"
128
 
#define HOOKDIR "/lib/mandos/network-hooks.d"
 
75
static const char *keydir = "/conf/conf.d/mandos";
 
76
static const char *pubkeyfile = "pubkey.txt";
 
77
static const char *seckeyfile = "seckey.txt";
129
78
 
130
79
bool debug = false;
131
 
static const char mandos_protocol_version[] = "1";
132
 
const char *argp_program_version = "mandos-client " VERSION;
133
 
const char *argp_program_bug_address = "<mandos@recompile.se>";
134
 
static const char sys_class_net[] = "/sys/class/net";
135
 
char *connect_to = NULL;
136
 
 
137
 
/* Doubly linked list that need to be circularly linked when used */
138
 
typedef struct server{
139
 
  const char *ip;
140
 
  uint16_t port;
141
 
  AvahiIfIndex if_index;
142
 
  int af;
143
 
  struct timespec last_seen;
144
 
  struct server *next;
145
 
  struct server *prev;
146
 
} server;
147
 
 
148
 
/* Used for passing in values through the Avahi callback functions */
 
80
 
 
81
/* Used for passing in values through all the callback functions */
149
82
typedef struct {
150
83
  AvahiSimplePoll *simple_poll;
151
84
  AvahiServer *server;
152
85
  gnutls_certificate_credentials_t cred;
153
86
  unsigned int dh_bits;
154
 
  gnutls_dh_params_t dh_params;
155
87
  const char *priority;
 
88
} mandos_context;
 
89
 
 
90
/* 
 
91
 * Decrypt OpenPGP data using keyrings in HOMEDIR.
 
92
 * Returns -1 on error
 
93
 */
 
94
static ssize_t pgp_packet_decrypt (const char *cryptotext,
 
95
                                   size_t crypto_size,
 
96
                                   char **plaintext,
 
97
                                   const char *homedir){
 
98
  gpgme_data_t dh_crypto, dh_plain;
156
99
  gpgme_ctx_t ctx;
157
 
  server *current_server;
158
 
} mandos_context;
159
 
 
160
 
/* global context so signal handler can reach it*/
161
 
mandos_context mc = { .simple_poll = NULL, .server = NULL,
162
 
                      .dh_bits = 1024, .priority = "SECURE256"
163
 
                      ":!CTYPE-X.509:+CTYPE-OPENPGP",
164
 
                      .current_server = NULL };
165
 
 
166
 
sig_atomic_t quit_now = 0;
167
 
int signal_received = 0;
168
 
 
169
 
/* Function to use when printing errors */
170
 
void perror_plus(const char *print_text){
171
 
  fprintf(stderr, "Mandos plugin %s: ",
172
 
          program_invocation_short_name);
173
 
  perror(print_text);
174
 
}
175
 
 
176
 
/*
177
 
 * Make additional room in "buffer" for at least BUFFER_SIZE more
178
 
 * bytes. "buffer_capacity" is how much is currently allocated,
179
 
 * "buffer_length" is how much is already used.
180
 
 */
181
 
size_t incbuffer(char **buffer, size_t buffer_length,
182
 
                  size_t buffer_capacity){
183
 
  if(buffer_length + BUFFER_SIZE > buffer_capacity){
184
 
    *buffer = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
185
 
    if(buffer == NULL){
186
 
      return 0;
187
 
    }
188
 
    buffer_capacity += BUFFER_SIZE;
189
 
  }
190
 
  return buffer_capacity;
191
 
}
192
 
 
193
 
/* Add server to set of servers to retry periodically */
194
 
int add_server(const char *ip, uint16_t port,
195
 
                 AvahiIfIndex if_index,
196
 
                 int af){
197
 
  int ret;
198
 
  server *new_server = malloc(sizeof(server));
199
 
  if(new_server == NULL){
200
 
    perror_plus("malloc");
201
 
    return -1;
202
 
  }
203
 
  *new_server = (server){ .ip = strdup(ip),
204
 
                         .port = port,
205
 
                         .if_index = if_index,
206
 
                         .af = af };
207
 
  if(new_server->ip == NULL){
208
 
    perror_plus("strdup");
209
 
    return -1;
210
 
  }
211
 
  /* Special case of first server */
212
 
  if (mc.current_server == NULL){
213
 
    new_server->next = new_server;
214
 
    new_server->prev = new_server;
215
 
    mc.current_server = new_server;
216
 
  /* Place the new server last in the list */
217
 
  } else {
218
 
    new_server->next = mc.current_server;
219
 
    new_server->prev = mc.current_server->prev;
220
 
    new_server->prev->next = new_server;
221
 
    mc.current_server->prev = new_server;
222
 
  }
223
 
  ret = clock_gettime(CLOCK_MONOTONIC, &mc.current_server->last_seen);
224
 
  if(ret == -1){
225
 
    perror_plus("clock_gettime");
226
 
    return -1;
227
 
  }
228
 
  return 0;
229
 
}
230
 
 
231
 
/* 
232
 
 * Initialize GPGME.
233
 
 */
234
 
static bool init_gpgme(const char *seckey,
235
 
                       const char *pubkey, const char *tempdir){
236
100
  gpgme_error_t rc;
 
101
  ssize_t ret;
 
102
  ssize_t plaintext_capacity = 0;
 
103
  ssize_t plaintext_length = 0;
237
104
  gpgme_engine_info_t engine_info;
238
105
  
239
 
  
240
 
  /*
241
 
   * Helper function to insert pub and seckey to the engine keyring.
242
 
   */
243
 
  bool import_key(const char *filename){
244
 
    int ret;
245
 
    int fd;
246
 
    gpgme_data_t pgp_data;
247
 
    
248
 
    fd = (int)TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
249
 
    if(fd == -1){
250
 
      perror_plus("open");
251
 
      return false;
252
 
    }
253
 
    
254
 
    rc = gpgme_data_new_from_fd(&pgp_data, fd);
255
 
    if(rc != GPG_ERR_NO_ERROR){
256
 
      fprintf(stderr, "Mandos plugin mandos-client: "
257
 
              "bad gpgme_data_new_from_fd: %s: %s\n",
258
 
              gpgme_strsource(rc), gpgme_strerror(rc));
259
 
      return false;
260
 
    }
261
 
    
262
 
    rc = gpgme_op_import(mc.ctx, pgp_data);
263
 
    if(rc != GPG_ERR_NO_ERROR){
264
 
      fprintf(stderr, "Mandos plugin mandos-client: "
265
 
              "bad gpgme_op_import: %s: %s\n",
266
 
              gpgme_strsource(rc), gpgme_strerror(rc));
267
 
      return false;
268
 
    }
269
 
    
270
 
    ret = (int)TEMP_FAILURE_RETRY(close(fd));
271
 
    if(ret == -1){
272
 
      perror_plus("close");
273
 
    }
274
 
    gpgme_data_release(pgp_data);
275
 
    return true;
276
 
  }
277
 
  
278
 
  if(debug){
279
 
    fprintf(stderr, "Mandos plugin mandos-client: "
280
 
            "Initializing GPGME\n");
 
106
  if (debug){
 
107
    fprintf(stderr, "Trying to decrypt OpenPGP data\n");
281
108
  }
282
109
  
283
110
  /* Init GPGME */
284
111
  gpgme_check_version(NULL);
285
112
  rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
286
 
  if(rc != GPG_ERR_NO_ERROR){
287
 
    fprintf(stderr, "Mandos plugin mandos-client: "
288
 
            "bad gpgme_engine_check_version: %s: %s\n",
 
113
  if (rc != GPG_ERR_NO_ERROR){
 
114
    fprintf(stderr, "bad gpgme_engine_check_version: %s: %s\n",
289
115
            gpgme_strsource(rc), gpgme_strerror(rc));
290
 
    return false;
 
116
    return -1;
291
117
  }
292
118
  
293
119
  /* Set GPGME home directory for the OpenPGP engine only */
294
 
  rc = gpgme_get_engine_info(&engine_info);
295
 
  if(rc != GPG_ERR_NO_ERROR){
296
 
    fprintf(stderr, "Mandos plugin mandos-client: "
297
 
            "bad gpgme_get_engine_info: %s: %s\n",
 
120
  rc = gpgme_get_engine_info (&engine_info);
 
121
  if (rc != GPG_ERR_NO_ERROR){
 
122
    fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
298
123
            gpgme_strsource(rc), gpgme_strerror(rc));
299
 
    return false;
 
124
    return -1;
300
125
  }
301
126
  while(engine_info != NULL){
302
127
    if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
303
128
      gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
304
 
                            engine_info->file_name, tempdir);
 
129
                            engine_info->file_name, homedir);
305
130
      break;
306
131
    }
307
132
    engine_info = engine_info->next;
308
133
  }
309
134
  if(engine_info == NULL){
310
 
    fprintf(stderr, "Mandos plugin mandos-client: "
311
 
            "Could not set GPGME home dir to %s\n", tempdir);
312
 
    return false;
313
 
  }
314
 
  
315
 
  /* Create new GPGME "context" */
316
 
  rc = gpgme_new(&(mc.ctx));
317
 
  if(rc != GPG_ERR_NO_ERROR){
318
 
    fprintf(stderr, "Mandos plugin mandos-client: "
319
 
            "bad gpgme_new: %s: %s\n", gpgme_strsource(rc),
320
 
            gpgme_strerror(rc));
321
 
    return false;
322
 
  }
323
 
  
324
 
  if(not import_key(pubkey) or not import_key(seckey)){
325
 
    return false;
326
 
  }
327
 
  
328
 
  return true;
329
 
}
330
 
 
331
 
/* 
332
 
 * Decrypt OpenPGP data.
333
 
 * Returns -1 on error
334
 
 */
335
 
static ssize_t pgp_packet_decrypt(const char *cryptotext,
336
 
                                  size_t crypto_size,
337
 
                                  char **plaintext){
338
 
  gpgme_data_t dh_crypto, dh_plain;
339
 
  gpgme_error_t rc;
340
 
  ssize_t ret;
341
 
  size_t plaintext_capacity = 0;
342
 
  ssize_t plaintext_length = 0;
343
 
  
344
 
  if(debug){
345
 
    fprintf(stderr, "Mandos plugin mandos-client: "
346
 
            "Trying to decrypt OpenPGP data\n");
 
135
    fprintf(stderr, "Could not set GPGME home dir to %s\n", homedir);
 
136
    return -1;
347
137
  }
348
138
  
349
139
  /* Create new GPGME data buffer from memory cryptotext */
350
140
  rc = gpgme_data_new_from_mem(&dh_crypto, cryptotext, crypto_size,
351
141
                               0);
352
 
  if(rc != GPG_ERR_NO_ERROR){
353
 
    fprintf(stderr, "Mandos plugin mandos-client: "
354
 
            "bad gpgme_data_new_from_mem: %s: %s\n",
 
142
  if (rc != GPG_ERR_NO_ERROR){
 
143
    fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
355
144
            gpgme_strsource(rc), gpgme_strerror(rc));
356
145
    return -1;
357
146
  }
358
147
  
359
148
  /* Create new empty GPGME data buffer for the plaintext */
360
149
  rc = gpgme_data_new(&dh_plain);
361
 
  if(rc != GPG_ERR_NO_ERROR){
362
 
    fprintf(stderr, "Mandos plugin mandos-client: "
363
 
            "bad gpgme_data_new: %s: %s\n",
 
150
  if (rc != GPG_ERR_NO_ERROR){
 
151
    fprintf(stderr, "bad gpgme_data_new: %s: %s\n",
364
152
            gpgme_strsource(rc), gpgme_strerror(rc));
365
153
    gpgme_data_release(dh_crypto);
366
154
    return -1;
367
155
  }
368
156
  
 
157
  /* Create new GPGME "context" */
 
158
  rc = gpgme_new(&ctx);
 
159
  if (rc != GPG_ERR_NO_ERROR){
 
160
    fprintf(stderr, "bad gpgme_new: %s: %s\n",
 
161
            gpgme_strsource(rc), gpgme_strerror(rc));
 
162
    plaintext_length = -1;
 
163
    goto decrypt_end;
 
164
  }
 
165
  
369
166
  /* Decrypt data from the cryptotext data buffer to the plaintext
370
167
     data buffer */
371
 
  rc = gpgme_op_decrypt(mc.ctx, dh_crypto, dh_plain);
372
 
  if(rc != GPG_ERR_NO_ERROR){
373
 
    fprintf(stderr, "Mandos plugin mandos-client: "
374
 
            "bad gpgme_op_decrypt: %s: %s\n",
 
168
  rc = gpgme_op_decrypt(ctx, dh_crypto, dh_plain);
 
169
  if (rc != GPG_ERR_NO_ERROR){
 
170
    fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
375
171
            gpgme_strsource(rc), gpgme_strerror(rc));
376
172
    plaintext_length = -1;
377
 
    if(debug){
378
 
      gpgme_decrypt_result_t result;
379
 
      result = gpgme_op_decrypt_result(mc.ctx);
380
 
      if(result == NULL){
381
 
        fprintf(stderr, "Mandos plugin mandos-client: "
382
 
                "gpgme_op_decrypt_result failed\n");
383
 
      } else {
384
 
        fprintf(stderr, "Mandos plugin mandos-client: "
385
 
                "Unsupported algorithm: %s\n",
386
 
                result->unsupported_algorithm);
387
 
        fprintf(stderr, "Mandos plugin mandos-client: "
388
 
                "Wrong key usage: %u\n",
389
 
                result->wrong_key_usage);
390
 
        if(result->file_name != NULL){
391
 
          fprintf(stderr, "Mandos plugin mandos-client: "
392
 
                  "File name: %s\n", result->file_name);
393
 
        }
394
 
        gpgme_recipient_t recipient;
395
 
        recipient = result->recipients;
 
173
    goto decrypt_end;
 
174
  }
 
175
  
 
176
  if(debug){
 
177
    fprintf(stderr, "Decryption of OpenPGP data succeeded\n");
 
178
  }
 
179
  
 
180
  if (debug){
 
181
    gpgme_decrypt_result_t result;
 
182
    result = gpgme_op_decrypt_result(ctx);
 
183
    if (result == NULL){
 
184
      fprintf(stderr, "gpgme_op_decrypt_result failed\n");
 
185
    } else {
 
186
      fprintf(stderr, "Unsupported algorithm: %s\n",
 
187
              result->unsupported_algorithm);
 
188
      fprintf(stderr, "Wrong key usage: %d\n",
 
189
              result->wrong_key_usage);
 
190
      if(result->file_name != NULL){
 
191
        fprintf(stderr, "File name: %s\n", result->file_name);
 
192
      }
 
193
      gpgme_recipient_t recipient;
 
194
      recipient = result->recipients;
 
195
      if(recipient){
396
196
        while(recipient != NULL){
397
 
          fprintf(stderr, "Mandos plugin mandos-client: "
398
 
                  "Public key algorithm: %s\n",
 
197
          fprintf(stderr, "Public key algorithm: %s\n",
399
198
                  gpgme_pubkey_algo_name(recipient->pubkey_algo));
400
 
          fprintf(stderr, "Mandos plugin mandos-client: "
401
 
                  "Key ID: %s\n", recipient->keyid);
402
 
          fprintf(stderr, "Mandos plugin mandos-client: "
403
 
                  "Secret key available: %s\n",
 
199
          fprintf(stderr, "Key ID: %s\n", recipient->keyid);
 
200
          fprintf(stderr, "Secret key available: %s\n",
404
201
                  recipient->status == GPG_ERR_NO_SECKEY
405
202
                  ? "No" : "Yes");
406
203
          recipient = recipient->next;
407
204
        }
408
205
      }
409
206
    }
410
 
    goto decrypt_end;
411
 
  }
412
 
  
413
 
  if(debug){
414
 
    fprintf(stderr, "Mandos plugin mandos-client: "
415
 
            "Decryption of OpenPGP data succeeded\n");
416
207
  }
417
208
  
418
209
  /* Seek back to the beginning of the GPGME plaintext data buffer */
419
 
  if(gpgme_data_seek(dh_plain, (off_t)0, SEEK_SET) == -1){
420
 
    perror_plus("gpgme_data_seek");
 
210
  if (gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET) == -1){
 
211
    perror("pgpme_data_seek");
421
212
    plaintext_length = -1;
422
213
    goto decrypt_end;
423
214
  }
424
215
  
425
216
  *plaintext = NULL;
426
217
  while(true){
427
 
    plaintext_capacity = incbuffer(plaintext,
428
 
                                      (size_t)plaintext_length,
429
 
                                      plaintext_capacity);
430
 
    if(plaintext_capacity == 0){
431
 
        perror_plus("incbuffer");
 
218
    if (plaintext_length + BUFFER_SIZE > plaintext_capacity){
 
219
      *plaintext = realloc(*plaintext,
 
220
                            (unsigned int)plaintext_capacity
 
221
                            + BUFFER_SIZE);
 
222
      if (*plaintext == NULL){
 
223
        perror("realloc");
432
224
        plaintext_length = -1;
433
225
        goto decrypt_end;
 
226
      }
 
227
      plaintext_capacity += BUFFER_SIZE;
434
228
    }
435
229
    
436
230
    ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
437
231
                          BUFFER_SIZE);
438
232
    /* Print the data, if any */
439
 
    if(ret == 0){
 
233
    if (ret == 0){
440
234
      /* EOF */
441
235
      break;
442
236
    }
443
237
    if(ret < 0){
444
 
      perror_plus("gpgme_data_read");
 
238
      perror("gpgme_data_read");
445
239
      plaintext_length = -1;
446
240
      goto decrypt_end;
447
241
    }
448
242
    plaintext_length += ret;
449
243
  }
450
 
  
 
244
 
451
245
  if(debug){
452
 
    fprintf(stderr, "Mandos plugin mandos-client: "
453
 
            "Decrypted password is: ");
454
 
    for(ssize_t i = 0; i < plaintext_length; i++){
 
246
    fprintf(stderr, "Decrypted password is: ");
 
247
    for(size_t i = 0; i < plaintext_length; i++){
455
248
      fprintf(stderr, "%02hhX ", (*plaintext)[i]);
456
249
    }
457
250
    fprintf(stderr, "\n");
467
260
  return plaintext_length;
468
261
}
469
262
 
470
 
static const char * safer_gnutls_strerror(int value){
471
 
  const char *ret = gnutls_strerror(value); /* Spurious warning from
472
 
                                               -Wunreachable-code */
473
 
  if(ret == NULL)
 
263
static const char * safer_gnutls_strerror (int value) {
 
264
  const char *ret = gnutls_strerror (value);
 
265
  if (ret == NULL)
474
266
    ret = "(unknown)";
475
267
  return ret;
476
268
}
477
269
 
478
 
/* GnuTLS log function callback */
479
270
static void debuggnutls(__attribute__((unused)) int level,
480
271
                        const char* string){
481
 
  fprintf(stderr, "Mandos plugin mandos-client: GnuTLS: %s", string);
 
272
  fprintf(stderr, "%s", string);
482
273
}
483
274
 
484
 
static int init_gnutls_global(const char *pubkeyfilename,
485
 
                              const char *seckeyfilename){
 
275
static int initgnutls(mandos_context *mc, gnutls_session_t *session,
 
276
                      gnutls_dh_params_t *dh_params){
 
277
  const char *err;
486
278
  int ret;
487
279
  
488
280
  if(debug){
489
 
    fprintf(stderr, "Mandos plugin mandos-client: "
490
 
            "Initializing GnuTLS\n");
 
281
    fprintf(stderr, "Initializing GnuTLS\n");
491
282
  }
492
 
  
493
 
  ret = gnutls_global_init();
494
 
  if(ret != GNUTLS_E_SUCCESS){
495
 
    fprintf(stderr, "Mandos plugin mandos-client: "
496
 
            "GnuTLS global_init: %s\n", safer_gnutls_strerror(ret));
 
283
 
 
284
  if ((ret = gnutls_global_init ())
 
285
      != GNUTLS_E_SUCCESS) {
 
286
    fprintf (stderr, "global_init: %s\n", safer_gnutls_strerror(ret));
497
287
    return -1;
498
288
  }
499
289
  
500
 
  if(debug){
501
 
    /* "Use a log level over 10 to enable all debugging options."
502
 
     * - GnuTLS manual
503
 
     */
 
290
  if (debug){
504
291
    gnutls_global_set_log_level(11);
505
292
    gnutls_global_set_log_function(debuggnutls);
506
293
  }
507
294
  
508
 
  /* OpenPGP credentials */
509
 
  ret = gnutls_certificate_allocate_credentials(&mc.cred);
510
 
  if(ret != GNUTLS_E_SUCCESS){
511
 
    fprintf(stderr, "Mandos plugin mandos-client: "
512
 
            "GnuTLS memory error: %s\n", safer_gnutls_strerror(ret));
513
 
    gnutls_global_deinit();
 
295
  /* openpgp credentials */
 
296
  if ((ret = gnutls_certificate_allocate_credentials (&mc->cred))
 
297
      != GNUTLS_E_SUCCESS) {
 
298
    fprintf (stderr, "memory error: %s\n",
 
299
             safer_gnutls_strerror(ret));
514
300
    return -1;
515
301
  }
516
302
  
517
303
  if(debug){
518
 
    fprintf(stderr, "Mandos plugin mandos-client: "
519
 
            "Attempting to use OpenPGP public key %s and"
520
 
            " secret key %s as GnuTLS credentials\n", pubkeyfilename,
521
 
            seckeyfilename);
 
304
    fprintf(stderr, "Attempting to use OpenPGP certificate %s"
 
305
            " and keyfile %s as GnuTLS credentials\n", pubkeyfile,
 
306
            seckeyfile);
522
307
  }
523
308
  
524
309
  ret = gnutls_certificate_set_openpgp_key_file
525
 
    (mc.cred, pubkeyfilename, seckeyfilename,
526
 
     GNUTLS_OPENPGP_FMT_BASE64);
527
 
  if(ret != GNUTLS_E_SUCCESS){
528
 
    fprintf(stderr,
529
 
            "Mandos plugin mandos-client: "
530
 
            "Error[%d] while reading the OpenPGP key pair ('%s',"
531
 
            " '%s')\n", ret, pubkeyfilename, seckeyfilename);
532
 
    fprintf(stderr, "Mandos plugin mandos-client: "
533
 
            "The GnuTLS error is: %s\n", safer_gnutls_strerror(ret));
534
 
    goto globalfail;
535
 
  }
536
 
  
537
 
  /* GnuTLS server initialization */
538
 
  ret = gnutls_dh_params_init(&mc.dh_params);
539
 
  if(ret != GNUTLS_E_SUCCESS){
540
 
    fprintf(stderr, "Mandos plugin mandos-client: "
541
 
            "Error in GnuTLS DH parameter initialization:"
542
 
            " %s\n", safer_gnutls_strerror(ret));
543
 
    goto globalfail;
544
 
  }
545
 
  ret = gnutls_dh_params_generate2(mc.dh_params, mc.dh_bits);
546
 
  if(ret != GNUTLS_E_SUCCESS){
547
 
    fprintf(stderr, "Mandos plugin mandos-client: "
548
 
            "Error in GnuTLS prime generation: %s\n",
549
 
            safer_gnutls_strerror(ret));
550
 
    goto globalfail;
551
 
  }
552
 
  
553
 
  gnutls_certificate_set_dh_params(mc.cred, mc.dh_params);
554
 
  
555
 
  return 0;
556
 
  
557
 
 globalfail:
558
 
  
559
 
  gnutls_certificate_free_credentials(mc.cred);
560
 
  gnutls_global_deinit();
561
 
  gnutls_dh_params_deinit(mc.dh_params);
562
 
  return -1;
563
 
}
564
 
 
565
 
static int init_gnutls_session(gnutls_session_t *session){
566
 
  int ret;
567
 
  /* GnuTLS session creation */
568
 
  do {
569
 
    ret = gnutls_init(session, GNUTLS_SERVER);
570
 
    if(quit_now){
571
 
      return -1;
572
 
    }
573
 
  } while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
574
 
  if(ret != GNUTLS_E_SUCCESS){
575
 
    fprintf(stderr, "Mandos plugin mandos-client: "
576
 
            "Error in GnuTLS session initialization: %s\n",
577
 
            safer_gnutls_strerror(ret));
578
 
  }
579
 
  
580
 
  {
581
 
    const char *err;
582
 
    do {
583
 
      ret = gnutls_priority_set_direct(*session, mc.priority, &err);
584
 
      if(quit_now){
585
 
        gnutls_deinit(*session);
586
 
        return -1;
587
 
      }
588
 
    } while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
589
 
    if(ret != GNUTLS_E_SUCCESS){
590
 
      fprintf(stderr, "Mandos plugin mandos-client: "
591
 
              "Syntax error at: %s\n", err);
592
 
      fprintf(stderr, "Mandos plugin mandos-client: "
593
 
              "GnuTLS error: %s\n", safer_gnutls_strerror(ret));
594
 
      gnutls_deinit(*session);
595
 
      return -1;
596
 
    }
597
 
  }
598
 
  
599
 
  do {
600
 
    ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
601
 
                                 mc.cred);
602
 
    if(quit_now){
603
 
      gnutls_deinit(*session);
604
 
      return -1;
605
 
    }
606
 
  } while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
607
 
  if(ret != GNUTLS_E_SUCCESS){
608
 
    fprintf(stderr, "Mandos plugin mandos-client: "
609
 
            "Error setting GnuTLS credentials: %s\n",
610
 
            safer_gnutls_strerror(ret));
611
 
    gnutls_deinit(*session);
 
310
    (mc->cred, pubkeyfile, seckeyfile, GNUTLS_OPENPGP_FMT_BASE64);
 
311
  if (ret != GNUTLS_E_SUCCESS) {
 
312
    fprintf
 
313
      (stderr, "Error[%d] while reading the OpenPGP key pair ('%s',"
 
314
       " '%s')\n",
 
315
       ret, pubkeyfile, seckeyfile);
 
316
    fprintf(stdout, "The Error is: %s\n",
 
317
            safer_gnutls_strerror(ret));
 
318
    return -1;
 
319
  }
 
320
  
 
321
  //GnuTLS server initialization
 
322
  if ((ret = gnutls_dh_params_init(dh_params))
 
323
      != GNUTLS_E_SUCCESS) {
 
324
    fprintf (stderr, "Error in dh parameter initialization: %s\n",
 
325
             safer_gnutls_strerror(ret));
 
326
    return -1;
 
327
  }
 
328
  
 
329
  if ((ret = gnutls_dh_params_generate2(*dh_params, mc->dh_bits))
 
330
      != GNUTLS_E_SUCCESS) {
 
331
    fprintf (stderr, "Error in prime generation: %s\n",
 
332
             safer_gnutls_strerror(ret));
 
333
    return -1;
 
334
  }
 
335
  
 
336
  gnutls_certificate_set_dh_params(mc->cred, *dh_params);
 
337
  
 
338
  // GnuTLS session creation
 
339
  if ((ret = gnutls_init(session, GNUTLS_SERVER))
 
340
      != GNUTLS_E_SUCCESS){
 
341
    fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
 
342
            safer_gnutls_strerror(ret));
 
343
  }
 
344
  
 
345
  if ((ret = gnutls_priority_set_direct(*session, mc->priority, &err))
 
346
      != GNUTLS_E_SUCCESS) {
 
347
    fprintf(stderr, "Syntax error at: %s\n", err);
 
348
    fprintf(stderr, "GnuTLS error: %s\n",
 
349
            safer_gnutls_strerror(ret));
 
350
    return -1;
 
351
  }
 
352
  
 
353
  if ((ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
 
354
                                    mc->cred))
 
355
      != GNUTLS_E_SUCCESS) {
 
356
    fprintf(stderr, "Error setting a credentials set: %s\n",
 
357
            safer_gnutls_strerror(ret));
612
358
    return -1;
613
359
  }
614
360
  
615
361
  /* ignore client certificate if any. */
616
 
  gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE);
 
362
  gnutls_certificate_server_set_request (*session,
 
363
                                         GNUTLS_CERT_IGNORE);
617
364
  
618
 
  gnutls_dh_set_prime_bits(*session, mc.dh_bits);
 
365
  gnutls_dh_set_prime_bits (*session, mc->dh_bits);
619
366
  
620
367
  return 0;
621
368
}
622
369
 
623
 
/* Avahi log function callback */
624
370
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
625
371
                      __attribute__((unused)) const char *txt){}
626
372
 
627
 
/* Called when a Mandos server is found */
628
373
static int start_mandos_communication(const char *ip, uint16_t port,
629
374
                                      AvahiIfIndex if_index,
630
 
                                      int af){
631
 
  int ret, tcp_sd = -1;
632
 
  ssize_t sret;
633
 
  union {
634
 
    struct sockaddr_in in;
635
 
    struct sockaddr_in6 in6;
636
 
  } to;
 
375
                                      mandos_context *mc){
 
376
  int ret, tcp_sd;
 
377
  struct sockaddr_in6 to;
637
378
  char *buffer = NULL;
638
 
  char *decrypted_buffer = NULL;
 
379
  char *decrypted_buffer;
639
380
  size_t buffer_length = 0;
640
381
  size_t buffer_capacity = 0;
641
 
  size_t written;
642
 
  int retval = -1;
 
382
  ssize_t decrypted_buffer_size;
 
383
  size_t written = 0;
 
384
  int retval = 0;
 
385
  char interface[IF_NAMESIZE];
643
386
  gnutls_session_t session;
644
 
  int pf;                       /* Protocol family */
645
 
  
646
 
  errno = 0;
647
 
  
648
 
  if(quit_now){
649
 
    errno = EINTR;
650
 
    return -1;
651
 
  }
652
 
  
653
 
  switch(af){
654
 
  case AF_INET6:
655
 
    pf = PF_INET6;
656
 
    break;
657
 
  case AF_INET:
658
 
    pf = PF_INET;
659
 
    break;
660
 
  default:
661
 
    fprintf(stderr, "Mandos plugin mandos-client: "
662
 
            "Bad address family: %d\n", af);
663
 
    errno = EINVAL;
664
 
    return -1;
665
 
  }
666
 
  
667
 
  ret = init_gnutls_session(&session);
668
 
  if(ret != 0){
669
 
    return -1;
670
 
  }
671
 
  
672
 
  if(debug){
673
 
    fprintf(stderr, "Mandos plugin mandos-client: "
674
 
            "Setting up a TCP connection to %s, port %" PRIu16
675
 
            "\n", ip, port);
676
 
  }
677
 
  
678
 
  tcp_sd = socket(pf, SOCK_STREAM, 0);
679
 
  if(tcp_sd < 0){
680
 
    int e = errno;
681
 
    perror_plus("socket");
682
 
    errno = e;
683
 
    goto mandos_end;
684
 
  }
685
 
  
686
 
  if(quit_now){
687
 
    errno = EINTR;
688
 
    goto mandos_end;
689
 
  }
690
 
  
691
 
  memset(&to, 0, sizeof(to));
692
 
  if(af == AF_INET6){
693
 
    to.in6.sin6_family = (sa_family_t)af;
694
 
    ret = inet_pton(af, ip, &to.in6.sin6_addr);
695
 
  } else {                      /* IPv4 */
696
 
    to.in.sin_family = (sa_family_t)af;
697
 
    ret = inet_pton(af, ip, &to.in.sin_addr);
698
 
  }
699
 
  if(ret < 0 ){
700
 
    int e = errno;
701
 
    perror_plus("inet_pton");
702
 
    errno = e;
703
 
    goto mandos_end;
 
387
  gnutls_dh_params_t dh_params;
 
388
  
 
389
  if(debug){
 
390
    fprintf(stderr, "Setting up a tcp connection to %s, port %d\n",
 
391
            ip, port);
 
392
  }
 
393
  
 
394
  tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
 
395
  if(tcp_sd < 0) {
 
396
    perror("socket");
 
397
    return -1;
 
398
  }
 
399
 
 
400
  if(debug){
 
401
    if(if_indextoname((unsigned int)if_index, interface) == NULL){
 
402
      perror("if_indextoname");
 
403
      return -1;
 
404
    }
 
405
    fprintf(stderr, "Binding to interface %s\n", interface);
 
406
  }
 
407
  
 
408
  memset(&to,0,sizeof(to));     /* Spurious warning */
 
409
  to.sin6_family = AF_INET6;
 
410
  ret = inet_pton(AF_INET6, ip, &to.sin6_addr);
 
411
  if (ret < 0 ){
 
412
    perror("inet_pton");
 
413
    return -1;
704
414
  }
705
415
  if(ret == 0){
706
 
    int e = errno;
707
 
    fprintf(stderr, "Mandos plugin mandos-client: "
708
 
            "Bad address: %s\n", ip);
709
 
    errno = e;
710
 
    goto mandos_end;
711
 
  }
712
 
  if(af == AF_INET6){
713
 
    to.in6.sin6_port = htons(port); /* Spurious warnings from
714
 
                                       -Wconversion and
715
 
                                       -Wunreachable-code */
716
 
    
717
 
    if(IN6_IS_ADDR_LINKLOCAL /* Spurious warnings from */
718
 
       (&to.in6.sin6_addr)){ /* -Wstrict-aliasing=2 or lower and
719
 
                              -Wunreachable-code*/
720
 
      if(if_index == AVAHI_IF_UNSPEC){
721
 
        fprintf(stderr, "Mandos plugin mandos-client: "
722
 
                "An IPv6 link-local address is incomplete"
723
 
                " without a network interface\n");
724
 
        errno = EINVAL;
725
 
        goto mandos_end;
726
 
      }
727
 
      /* Set the network interface number as scope */
728
 
      to.in6.sin6_scope_id = (uint32_t)if_index;
729
 
    }
730
 
  } else {
731
 
    to.in.sin_port = htons(port); /* Spurious warnings from
732
 
                                     -Wconversion and
733
 
                                     -Wunreachable-code */
734
 
  }
 
416
    fprintf(stderr, "Bad address: %s\n", ip);
 
417
    return -1;
 
418
  }
 
419
  to.sin6_port = htons(port);   /* Spurious warning */
735
420
  
736
 
  if(quit_now){
737
 
    errno = EINTR;
738
 
    goto mandos_end;
739
 
  }
 
421
  to.sin6_scope_id = (uint32_t)if_index;
740
422
  
741
423
  if(debug){
742
 
    if(af == AF_INET6 and if_index != AVAHI_IF_UNSPEC){
743
 
      char interface[IF_NAMESIZE];
744
 
      if(if_indextoname((unsigned int)if_index, interface) == NULL){
745
 
        perror_plus("if_indextoname");
746
 
      } else {
747
 
        fprintf(stderr, "Mandos plugin mandos-client: "
748
 
                "Connection to: %s%%%s, port %" PRIu16 "\n",
749
 
                ip, interface, port);
750
 
      }
751
 
    } else {
752
 
      fprintf(stderr, "Mandos plugin mandos-client: "
753
 
              "Connection to: %s, port %" PRIu16 "\n", ip, port);
754
 
    }
755
 
    char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ?
756
 
                 INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = "";
757
 
    const char *pcret;
758
 
    if(af == AF_INET6){
759
 
      pcret = inet_ntop(af, &(to.in6.sin6_addr), addrstr,
760
 
                        sizeof(addrstr));
761
 
    } else {
762
 
      pcret = inet_ntop(af, &(to.in.sin_addr), addrstr,
763
 
                        sizeof(addrstr));
764
 
    }
765
 
    if(pcret == NULL){
766
 
      perror_plus("inet_ntop");
 
424
    fprintf(stderr, "Connection to: %s, port %d\n", ip, port);
 
425
    char addrstr[INET6_ADDRSTRLEN] = "";
 
426
    if(inet_ntop(to.sin6_family, &(to.sin6_addr), addrstr,
 
427
                 sizeof(addrstr)) == NULL){
 
428
      perror("inet_ntop");
767
429
    } else {
768
430
      if(strcmp(addrstr, ip) != 0){
769
 
        fprintf(stderr, "Mandos plugin mandos-client: "
770
 
                "Canonical address form: %s\n", addrstr);
771
 
      }
772
 
    }
773
 
  }
774
 
  
775
 
  if(quit_now){
776
 
    errno = EINTR;
777
 
    goto mandos_end;
778
 
  }
779
 
  
780
 
  if(af == AF_INET6){
781
 
    ret = connect(tcp_sd, &to.in6, sizeof(to));
782
 
  } else {
783
 
    ret = connect(tcp_sd, &to.in, sizeof(to)); /* IPv4 */
784
 
  }
785
 
  if(ret < 0){
786
 
    if ((errno != ECONNREFUSED and errno != ENETUNREACH) or debug){
787
 
      int e = errno;
788
 
      perror_plus("connect");
789
 
      errno = e;
790
 
    }
791
 
    goto mandos_end;
792
 
  }
793
 
  
794
 
  if(quit_now){
795
 
    errno = EINTR;
796
 
    goto mandos_end;
797
 
  }
798
 
  
799
 
  const char *out = mandos_protocol_version;
800
 
  written = 0;
801
 
  while(true){
802
 
    size_t out_size = strlen(out);
803
 
    ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
804
 
                                   out_size - written));
805
 
    if(ret == -1){
806
 
      int e = errno;
807
 
      perror_plus("write");
808
 
      errno = e;
809
 
      goto mandos_end;
810
 
    }
811
 
    written += (size_t)ret;
812
 
    if(written < out_size){
813
 
      continue;
814
 
    } else {
815
 
      if(out == mandos_protocol_version){
816
 
        written = 0;
817
 
        out = "\r\n";
818
 
      } else {
819
 
        break;
820
 
      }
821
 
    }
822
 
  
823
 
    if(quit_now){
824
 
      errno = EINTR;
825
 
      goto mandos_end;
826
 
    }
827
 
  }
 
431
        fprintf(stderr, "Canonical address form: %s\n", addrstr);
 
432
      }
 
433
    }
 
434
  }
 
435
  
 
436
  ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
 
437
  if (ret < 0){
 
438
    perror("connect");
 
439
    return -1;
 
440
  }
 
441
  
 
442
  ret = initgnutls (mc, &session, &dh_params);
 
443
  if (ret != 0){
 
444
    retval = -1;
 
445
    return -1;
 
446
  }
 
447
  
 
448
  gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t) tcp_sd);
828
449
  
829
450
  if(debug){
830
 
    fprintf(stderr, "Mandos plugin mandos-client: "
831
 
            "Establishing TLS session with %s\n", ip);
832
 
  }
833
 
  
834
 
  if(quit_now){
835
 
    errno = EINTR;
836
 
    goto mandos_end;
837
 
  }
838
 
  
839
 
  /* Spurious warning from -Wint-to-pointer-cast */
840
 
  gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t) tcp_sd);
841
 
  
842
 
  if(quit_now){
843
 
    errno = EINTR;
844
 
    goto mandos_end;
845
 
  }
846
 
  
847
 
  do {
848
 
    ret = gnutls_handshake(session);
849
 
    if(quit_now){
850
 
      errno = EINTR;
851
 
      goto mandos_end;
852
 
    }
853
 
  } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
854
 
  
855
 
  if(ret != GNUTLS_E_SUCCESS){
 
451
    fprintf(stderr, "Establishing TLS session with %s\n", ip);
 
452
  }
 
453
  
 
454
  ret = gnutls_handshake (session);
 
455
  
 
456
  if (ret != GNUTLS_E_SUCCESS){
856
457
    if(debug){
857
 
      fprintf(stderr, "Mandos plugin mandos-client: "
858
 
              "*** GnuTLS Handshake failed ***\n");
859
 
      gnutls_perror(ret);
 
458
      fprintf(stderr, "\n*** Handshake failed ***\n");
 
459
      gnutls_perror (ret);
860
460
    }
861
 
    errno = EPROTO;
862
 
    goto mandos_end;
 
461
    retval = -1;
 
462
    goto exit;
863
463
  }
864
464
  
865
 
  /* Read OpenPGP packet that contains the wanted password */
 
465
  //Retrieve OpenPGP packet that contains the wanted password
866
466
  
867
467
  if(debug){
868
 
    fprintf(stderr, "Mandos plugin mandos-client: "
869
 
            "Retrieving OpenPGP encrypted password from %s\n", ip);
 
468
    fprintf(stderr, "Retrieving pgp encrypted password from %s\n",
 
469
            ip);
870
470
  }
871
 
  
 
471
 
872
472
  while(true){
873
 
    
874
 
    if(quit_now){
875
 
      errno = EINTR;
876
 
      goto mandos_end;
877
 
    }
878
 
    
879
 
    buffer_capacity = incbuffer(&buffer, buffer_length,
880
 
                                   buffer_capacity);
881
 
    if(buffer_capacity == 0){
882
 
      int e = errno;
883
 
      perror_plus("incbuffer");
884
 
      errno = e;
885
 
      goto mandos_end;
886
 
    }
887
 
    
888
 
    if(quit_now){
889
 
      errno = EINTR;
890
 
      goto mandos_end;
891
 
    }
892
 
    
893
 
    sret = gnutls_record_recv(session, buffer+buffer_length,
894
 
                              BUFFER_SIZE);
895
 
    if(sret == 0){
 
473
    if (buffer_length + BUFFER_SIZE > buffer_capacity){
 
474
      buffer = realloc(buffer, buffer_capacity + BUFFER_SIZE);
 
475
      if (buffer == NULL){
 
476
        perror("realloc");
 
477
        goto exit;
 
478
      }
 
479
      buffer_capacity += BUFFER_SIZE;
 
480
    }
 
481
    
 
482
    ret = gnutls_record_recv(session, buffer+buffer_length,
 
483
                             BUFFER_SIZE);
 
484
    if (ret == 0){
896
485
      break;
897
486
    }
898
 
    if(sret < 0){
899
 
      switch(sret){
 
487
    if (ret < 0){
 
488
      switch(ret){
900
489
      case GNUTLS_E_INTERRUPTED:
901
490
      case GNUTLS_E_AGAIN:
902
491
        break;
903
492
      case GNUTLS_E_REHANDSHAKE:
904
 
        do {
905
 
          ret = gnutls_handshake(session);
906
 
          
907
 
          if(quit_now){
908
 
            errno = EINTR;
909
 
            goto mandos_end;
910
 
          }
911
 
        } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
912
 
        if(ret < 0){
913
 
          fprintf(stderr, "Mandos plugin mandos-client: "
914
 
                  "*** GnuTLS Re-handshake failed ***\n");
915
 
          gnutls_perror(ret);
916
 
          errno = EPROTO;
917
 
          goto mandos_end;
 
493
        ret = gnutls_handshake (session);
 
494
        if (ret < 0){
 
495
          fprintf(stderr, "\n*** Handshake failed ***\n");
 
496
          gnutls_perror (ret);
 
497
          retval = -1;
 
498
          goto exit;
918
499
        }
919
500
        break;
920
501
      default:
921
 
        fprintf(stderr, "Mandos plugin mandos-client: "
922
 
                "Unknown error while reading data from"
923
 
                " encrypted session with Mandos server\n");
924
 
        gnutls_bye(session, GNUTLS_SHUT_RDWR);
925
 
        errno = EIO;
926
 
        goto mandos_end;
 
502
        fprintf(stderr, "Unknown error while reading data from"
 
503
                " encrypted session with mandos server\n");
 
504
        retval = -1;
 
505
        gnutls_bye (session, GNUTLS_SHUT_RDWR);
 
506
        goto exit;
927
507
      }
928
508
    } else {
929
 
      buffer_length += (size_t) sret;
930
 
    }
931
 
  }
932
 
  
933
 
  if(debug){
934
 
    fprintf(stderr, "Mandos plugin mandos-client: "
935
 
            "Closing TLS session\n");
936
 
  }
937
 
  
938
 
  if(quit_now){
939
 
    errno = EINTR;
940
 
    goto mandos_end;
941
 
  }
942
 
  
943
 
  do {
944
 
    ret = gnutls_bye(session, GNUTLS_SHUT_RDWR);
945
 
    if(quit_now){
946
 
      errno = EINTR;
947
 
      goto mandos_end;
948
 
    }
949
 
  } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
950
 
  
951
 
  if(buffer_length > 0){
952
 
    ssize_t decrypted_buffer_size;
 
509
      buffer_length += (size_t) ret;
 
510
    }
 
511
  }
 
512
  
 
513
  if (buffer_length > 0){
953
514
    decrypted_buffer_size = pgp_packet_decrypt(buffer,
954
515
                                               buffer_length,
955
 
                                               &decrypted_buffer);
956
 
    if(decrypted_buffer_size >= 0){
957
 
      
958
 
      written = 0;
 
516
                                               &decrypted_buffer,
 
517
                                               keydir);
 
518
    if (decrypted_buffer_size >= 0){
959
519
      while(written < (size_t) decrypted_buffer_size){
960
 
        if(quit_now){
961
 
          errno = EINTR;
962
 
          goto mandos_end;
963
 
        }
964
 
        
965
 
        ret = (int)fwrite(decrypted_buffer + written, 1,
966
 
                          (size_t)decrypted_buffer_size - written,
967
 
                          stdout);
 
520
        ret = (int)fwrite (decrypted_buffer + written, 1,
 
521
                           (size_t)decrypted_buffer_size - written,
 
522
                           stdout);
968
523
        if(ret == 0 and ferror(stdout)){
969
 
          int e = errno;
970
524
          if(debug){
971
 
            fprintf(stderr, "Mandos plugin mandos-client: "
972
 
                    "Error writing encrypted data: %s\n",
 
525
            fprintf(stderr, "Error writing encrypted data: %s\n",
973
526
                    strerror(errno));
974
527
          }
975
 
          errno = e;
976
 
          goto mandos_end;
 
528
          retval = -1;
 
529
          break;
977
530
        }
978
531
        written += (size_t)ret;
979
532
      }
980
 
      retval = 0;
981
 
    }
982
 
  }
983
 
  
984
 
  /* Shutdown procedure */
985
 
  
986
 
 mandos_end:
987
 
  {
988
 
    int e = errno;
989
 
    free(decrypted_buffer);
990
 
    free(buffer);
991
 
    if(tcp_sd >= 0){
992
 
      ret = (int)TEMP_FAILURE_RETRY(close(tcp_sd));
993
 
    }
994
 
    if(ret == -1){
995
 
      if(e == 0){
996
 
        e = errno;
997
 
      }
998
 
      perror_plus("close");
999
 
    }
1000
 
    gnutls_deinit(session);
1001
 
    errno = e;
1002
 
    if(quit_now){
1003
 
      errno = EINTR;
 
533
      free(decrypted_buffer);
 
534
    } else {
1004
535
      retval = -1;
1005
536
    }
1006
537
  }
 
538
 
 
539
  //shutdown procedure
 
540
 
 
541
  if(debug){
 
542
    fprintf(stderr, "Closing TLS session\n");
 
543
  }
 
544
 
 
545
  free(buffer);
 
546
  gnutls_bye (session, GNUTLS_SHUT_RDWR);
 
547
 exit:
 
548
  close(tcp_sd);
 
549
  gnutls_deinit (session);
 
550
  gnutls_certificate_free_credentials (mc->cred);
 
551
  gnutls_global_deinit ();
1007
552
  return retval;
1008
553
}
1009
554
 
1010
555
static void resolve_callback(AvahiSServiceResolver *r,
1011
556
                             AvahiIfIndex interface,
1012
 
                             AvahiProtocol proto,
 
557
                             AVAHI_GCC_UNUSED AvahiProtocol protocol,
1013
558
                             AvahiResolverEvent event,
1014
559
                             const char *name,
1015
560
                             const char *type,
1020
565
                             AVAHI_GCC_UNUSED AvahiStringList *txt,
1021
566
                             AVAHI_GCC_UNUSED AvahiLookupResultFlags
1022
567
                             flags,
1023
 
                             AVAHI_GCC_UNUSED void* userdata){
1024
 
  assert(r);
 
568
                             void* userdata) {
 
569
  mandos_context *mc = userdata;
 
570
  assert(r);                    /* Spurious warning */
1025
571
  
1026
572
  /* Called whenever a service has been resolved successfully or
1027
573
     timed out */
1028
574
  
1029
 
  if(quit_now){
1030
 
    return;
1031
 
  }
1032
 
  
1033
 
  switch(event){
 
575
  switch (event) {
1034
576
  default:
1035
577
  case AVAHI_RESOLVER_FAILURE:
1036
 
    fprintf(stderr, "Mandos plugin mandos-client: "
1037
 
            "(Avahi Resolver) Failed to resolve service '%s'"
1038
 
            " of type '%s' in domain '%s': %s\n", name, type, domain,
1039
 
            avahi_strerror(avahi_server_errno(mc.server)));
 
578
    fprintf(stderr, "(Resolver) Failed to resolve service '%s' of"
 
579
            " type '%s' in domain '%s': %s\n", name, type, domain,
 
580
            avahi_strerror(avahi_server_errno(mc->server)));
1040
581
    break;
1041
582
    
1042
583
  case AVAHI_RESOLVER_FOUND:
1044
585
      char ip[AVAHI_ADDRESS_STR_MAX];
1045
586
      avahi_address_snprint(ip, sizeof(ip), address);
1046
587
      if(debug){
1047
 
        fprintf(stderr, "Mandos plugin mandos-client: "
1048
 
                "Mandos server \"%s\" found on %s (%s, %"
1049
 
                PRIdMAX ") on port %" PRIu16 "\n", name, host_name,
1050
 
                ip, (intmax_t)interface, port);
 
588
        fprintf(stderr, "Mandos server \"%s\" found on %s (%s) on"
 
589
                " port %d\n", name, host_name, ip, port);
1051
590
      }
1052
 
      int ret = start_mandos_communication(ip, port, interface,
1053
 
                                           avahi_proto_to_af(proto));
1054
 
      if(ret == 0){
1055
 
        avahi_simple_poll_quit(mc.simple_poll);
1056
 
      } else {
1057
 
        ret = add_server(ip, port, interface,
1058
 
                         avahi_proto_to_af(proto));
 
591
      int ret = start_mandos_communication(ip, port, interface, mc);
 
592
      if (ret == 0){
 
593
        exit(EXIT_SUCCESS);
1059
594
      }
1060
595
    }
1061
596
  }
1062
597
  avahi_s_service_resolver_free(r);
1063
598
}
1064
599
 
1065
 
static void browse_callback(AvahiSServiceBrowser *b,
1066
 
                            AvahiIfIndex interface,
1067
 
                            AvahiProtocol protocol,
1068
 
                            AvahiBrowserEvent event,
1069
 
                            const char *name,
1070
 
                            const char *type,
1071
 
                            const char *domain,
1072
 
                            AVAHI_GCC_UNUSED AvahiLookupResultFlags
1073
 
                            flags,
1074
 
                            AVAHI_GCC_UNUSED void* userdata){
1075
 
  assert(b);
 
600
static void browse_callback( AvahiSServiceBrowser *b,
 
601
                             AvahiIfIndex interface,
 
602
                             AvahiProtocol protocol,
 
603
                             AvahiBrowserEvent event,
 
604
                             const char *name,
 
605
                             const char *type,
 
606
                             const char *domain,
 
607
                             AVAHI_GCC_UNUSED AvahiLookupResultFlags
 
608
                             flags,
 
609
                             void* userdata) {
 
610
  mandos_context *mc = userdata;
 
611
  assert(b);                    /* Spurious warning */
1076
612
  
1077
613
  /* Called whenever a new services becomes available on the LAN or
1078
614
     is removed from the LAN */
1079
615
  
1080
 
  if(quit_now){
1081
 
    return;
1082
 
  }
1083
 
  
1084
 
  switch(event){
 
616
  switch (event) {
1085
617
  default:
1086
618
  case AVAHI_BROWSER_FAILURE:
1087
619
    
1088
 
    fprintf(stderr, "Mandos plugin mandos-client: "
1089
 
            "(Avahi browser) %s\n",
1090
 
            avahi_strerror(avahi_server_errno(mc.server)));
1091
 
    avahi_simple_poll_quit(mc.simple_poll);
 
620
    fprintf(stderr, "(Browser) %s\n",
 
621
            avahi_strerror(avahi_server_errno(mc->server)));
 
622
    avahi_simple_poll_quit(mc->simple_poll);
1092
623
    return;
1093
624
    
1094
625
  case AVAHI_BROWSER_NEW:
1095
 
    /* We ignore the returned Avahi resolver object. In the callback
1096
 
       function we free it. If the Avahi server is terminated before
1097
 
       the callback function is called the Avahi server will free the
1098
 
       resolver for us. */
 
626
    /* We ignore the returned resolver object. In the callback
 
627
       function we free it. If the server is terminated before
 
628
       the callback function is called the server will free
 
629
       the resolver for us. */
1099
630
    
1100
 
    if(avahi_s_service_resolver_new(mc.server, interface, protocol,
1101
 
                                    name, type, domain, protocol, 0,
1102
 
                                    resolve_callback, NULL) == NULL)
1103
 
      fprintf(stderr, "Mandos plugin mandos-client: "
1104
 
              "Avahi: Failed to resolve service '%s': %s\n",
1105
 
              name, avahi_strerror(avahi_server_errno(mc.server)));
 
631
    if (!(avahi_s_service_resolver_new(mc->server, interface,
 
632
                                       protocol, name, type, domain,
 
633
                                       AVAHI_PROTO_INET6, 0,
 
634
                                       resolve_callback, mc)))
 
635
      fprintf(stderr, "Failed to resolve service '%s': %s\n", name,
 
636
              avahi_strerror(avahi_server_errno(mc->server)));
1106
637
    break;
1107
638
    
1108
639
  case AVAHI_BROWSER_REMOVE:
1110
641
    
1111
642
  case AVAHI_BROWSER_ALL_FOR_NOW:
1112
643
  case AVAHI_BROWSER_CACHE_EXHAUSTED:
1113
 
    if(debug){
1114
 
      fprintf(stderr, "Mandos plugin mandos-client: "
1115
 
              "No Mandos server found, still searching...\n");
1116
 
    }
1117
644
    break;
1118
645
  }
1119
646
}
1120
647
 
1121
 
/* Signal handler that stops main loop after SIGTERM */
1122
 
static void handle_sigterm(int sig){
1123
 
  if(quit_now){
1124
 
    return;
1125
 
  }
1126
 
  quit_now = 1;
1127
 
  signal_received = sig;
1128
 
  int old_errno = errno;
1129
 
  /* set main loop to exit */
1130
 
  if(mc.simple_poll != NULL){
1131
 
    avahi_simple_poll_quit(mc.simple_poll);
1132
 
  }
1133
 
  errno = old_errno;
1134
 
}
1135
 
 
1136
 
bool get_flags(const char *ifname, struct ifreq *ifr){
1137
 
  int ret;
1138
 
  
1139
 
  int s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1140
 
  if(s < 0){
1141
 
    perror_plus("socket");
1142
 
    return false;
1143
 
  }
1144
 
  strcpy(ifr->ifr_name, ifname);
1145
 
  ret = ioctl(s, SIOCGIFFLAGS, ifr);
1146
 
  if(ret == -1){
1147
 
    if(debug){
1148
 
      perror_plus("ioctl SIOCGIFFLAGS");
1149
 
    }
1150
 
    return false;
1151
 
  }
1152
 
  return true;
1153
 
}
1154
 
 
1155
 
bool good_flags(const char *ifname, const struct ifreq *ifr){
1156
 
  
1157
 
  /* Reject the loopback device */
1158
 
  if(ifr->ifr_flags & IFF_LOOPBACK){
1159
 
    if(debug){
1160
 
      fprintf(stderr, "Mandos plugin mandos-client: "
1161
 
              "Rejecting loopback interface \"%s\"\n", ifname);
1162
 
    }
1163
 
    return false;
1164
 
  }
1165
 
  /* Accept point-to-point devices only if connect_to is specified */
1166
 
  if(connect_to != NULL and (ifr->ifr_flags & IFF_POINTOPOINT)){
1167
 
    if(debug){
1168
 
      fprintf(stderr, "Mandos plugin mandos-client: "
1169
 
              "Accepting point-to-point interface \"%s\"\n", ifname);
1170
 
    }
1171
 
    return true;
1172
 
  }
1173
 
  /* Otherwise, reject non-broadcast-capable devices */
1174
 
  if(not (ifr->ifr_flags & IFF_BROADCAST)){
1175
 
    if(debug){
1176
 
      fprintf(stderr, "Mandos plugin mandos-client: "
1177
 
              "Rejecting non-broadcast interface \"%s\"\n", ifname);
1178
 
    }
1179
 
    return false;
1180
 
  }
1181
 
  /* Reject non-ARP interfaces (including dummy interfaces) */
1182
 
  if(ifr->ifr_flags & IFF_NOARP){
1183
 
    if(debug){
1184
 
      fprintf(stderr, "Mandos plugin mandos-client: "
1185
 
              "Rejecting non-ARP interface \"%s\"\n", ifname);
1186
 
    }
1187
 
    return false;
1188
 
  }
1189
 
  
1190
 
  /* Accept this device */
1191
 
  if(debug){
1192
 
    fprintf(stderr, "Mandos plugin mandos-client: "
1193
 
            "Interface \"%s\" is good\n", ifname);
1194
 
  }
1195
 
  return true;
1196
 
}
1197
 
 
1198
 
/* 
1199
 
 * This function determines if a directory entry in /sys/class/net
1200
 
 * corresponds to an acceptable network device.
1201
 
 * (This function is passed to scandir(3) as a filter function.)
1202
 
 */
1203
 
int good_interface(const struct dirent *if_entry){
1204
 
  if(if_entry->d_name[0] == '.'){
1205
 
    return 0;
1206
 
  }
1207
 
  
1208
 
  struct ifreq ifr;
1209
 
  if(not get_flags(if_entry->d_name, &ifr)){
1210
 
    if(debug){
1211
 
      fprintf(stderr, "Mandos plugin mandos-client: "
1212
 
              "Failed to get flags for interface \"%s\"\n",
1213
 
              if_entry->d_name);
1214
 
    }
1215
 
    return 0;
1216
 
  }
1217
 
  
1218
 
  if(not good_flags(if_entry->d_name, &ifr)){
1219
 
    return 0;
1220
 
  }
1221
 
  return 1;
1222
 
}
1223
 
 
1224
 
/* 
1225
 
 * This function determines if a directory entry in /sys/class/net
1226
 
 * corresponds to an acceptable network device which is up.
1227
 
 * (This function is passed to scandir(3) as a filter function.)
1228
 
 */
1229
 
int up_interface(const struct dirent *if_entry){
1230
 
  if(if_entry->d_name[0] == '.'){
1231
 
    return 0;
1232
 
  }
1233
 
  
1234
 
  struct ifreq ifr;
1235
 
  if(not get_flags(if_entry->d_name, &ifr)){
1236
 
    if(debug){
1237
 
      fprintf(stderr, "Mandos plugin mandos-client: "
1238
 
              "Failed to get flags for interface \"%s\"\n",
1239
 
              if_entry->d_name);
1240
 
    }
1241
 
    return 0;
1242
 
  }
1243
 
  
1244
 
  /* Reject down interfaces */
1245
 
  if(not (ifr.ifr_flags & IFF_UP)){
1246
 
    if(debug){
1247
 
      fprintf(stderr, "Mandos plugin mandos-client: "
1248
 
              "Rejecting down interface \"%s\"\n",
1249
 
              if_entry->d_name);
1250
 
    }
1251
 
    return 0;
1252
 
  }
1253
 
  
1254
 
  /* Reject non-running interfaces */
1255
 
  if(not (ifr.ifr_flags & IFF_RUNNING)){
1256
 
    if(debug){
1257
 
      fprintf(stderr, "Mandos plugin mandos-client: "
1258
 
              "Rejecting non-running interface \"%s\"\n",
1259
 
              if_entry->d_name);
1260
 
    }
1261
 
    return 0;
1262
 
  }
1263
 
  
1264
 
  if(not good_flags(if_entry->d_name, &ifr)){
1265
 
    return 0;
1266
 
  }
1267
 
  return 1;
1268
 
}
1269
 
 
1270
 
int notdotentries(const struct dirent *direntry){
1271
 
  /* Skip "." and ".." */
1272
 
  if(direntry->d_name[0] == '.'
1273
 
     and (direntry->d_name[1] == '\0'
1274
 
          or (direntry->d_name[1] == '.'
1275
 
              and direntry->d_name[2] == '\0'))){
1276
 
    return 0;
1277
 
  }
1278
 
  return 1;
1279
 
}
1280
 
 
1281
 
/* Is this directory entry a runnable program? */
1282
 
int runnable_hook(const struct dirent *direntry){
1283
 
  int ret;
1284
 
  struct stat st;
1285
 
  
1286
 
  if((direntry->d_name)[0] == '\0'){
1287
 
    /* Empty name? */
1288
 
    return 0;
1289
 
  }
1290
 
  
1291
 
  /* Save pointer to last character */
1292
 
  char *end = strchr(direntry->d_name, '\0')-1;
1293
 
  
1294
 
  if(*end == '~'){
1295
 
    /* Backup name~ */
1296
 
    return 0;
1297
 
  }
1298
 
  
1299
 
  if(((direntry->d_name)[0] == '#')
1300
 
     and (*end == '#')){
1301
 
    /* Temporary #name# */
1302
 
    return 0;
1303
 
  }
1304
 
  
1305
 
  /* XXX more rules here */
1306
 
  
1307
 
  ret = stat(direntry->d_name, &st);
1308
 
  if(ret == -1){
1309
 
    if(debug){
1310
 
      perror_plus("Could not stat plugin");
1311
 
    }
1312
 
    return 0;
1313
 
  }
1314
 
  if(not (S_ISREG(st.st_mode))){
1315
 
    /* Not a regular file */
1316
 
    return 0;
1317
 
  }
1318
 
  if(not (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))){
1319
 
    /* Not executable */
1320
 
    return 0;
1321
 
  }
1322
 
  return 1;
1323
 
}
1324
 
 
1325
 
int avahi_loop_with_timeout(AvahiSimplePoll *s, int retry_interval){
1326
 
  int ret;
1327
 
  struct timespec now;
1328
 
  struct timespec waited_time;
1329
 
  intmax_t block_time;
1330
 
  
1331
 
  while(true){
1332
 
    if(mc.current_server == NULL){
1333
 
      if (debug){
1334
 
        fprintf(stderr, "Mandos plugin mandos-client: "
1335
 
                "Wait until first server is found. No timeout!\n");
1336
 
      }
1337
 
      ret = avahi_simple_poll_iterate(s, -1);
1338
 
    } else {
1339
 
      if (debug){
1340
 
        fprintf(stderr, "Mandos plugin mandos-client: "
1341
 
                "Check current_server if we should run it,"
1342
 
                " or wait\n");
1343
 
      }
1344
 
      /* the current time */
1345
 
      ret = clock_gettime(CLOCK_MONOTONIC, &now);
1346
 
      if(ret == -1){
1347
 
        perror_plus("clock_gettime");
1348
 
        return -1;
1349
 
      }
1350
 
      /* Calculating in ms how long time between now and server
1351
 
         who we visted longest time ago. Now - last seen.  */
1352
 
      waited_time.tv_sec = (now.tv_sec
1353
 
                            - mc.current_server->last_seen.tv_sec);
1354
 
      waited_time.tv_nsec = (now.tv_nsec
1355
 
                             - mc.current_server->last_seen.tv_nsec);
1356
 
      /* total time is 10s/10,000ms.
1357
 
         Converting to s from ms by dividing by 1,000,
1358
 
         and ns to ms by dividing by 1,000,000. */
1359
 
      block_time = ((retry_interval
1360
 
                     - ((intmax_t)waited_time.tv_sec * 1000))
1361
 
                    - ((intmax_t)waited_time.tv_nsec / 1000000));
1362
 
      
1363
 
      if (debug){
1364
 
        fprintf(stderr, "Mandos plugin mandos-client: "
1365
 
                "Blocking for %" PRIdMAX " ms\n", block_time);
1366
 
      }
1367
 
      
1368
 
      if(block_time <= 0){
1369
 
        ret = start_mandos_communication(mc.current_server->ip,
1370
 
                                         mc.current_server->port,
1371
 
                                         mc.current_server->if_index,
1372
 
                                         mc.current_server->af);
1373
 
        if(ret == 0){
1374
 
          avahi_simple_poll_quit(mc.simple_poll);
1375
 
          return 0;
1376
 
        }
1377
 
        ret = clock_gettime(CLOCK_MONOTONIC,
1378
 
                            &mc.current_server->last_seen);
1379
 
        if(ret == -1){
1380
 
          perror_plus("clock_gettime");
1381
 
          return -1;
1382
 
        }
1383
 
        mc.current_server = mc.current_server->next;
1384
 
        block_time = 0;         /* Call avahi to find new Mandos
1385
 
                                   servers, but don't block */
1386
 
      }
1387
 
      
1388
 
      ret = avahi_simple_poll_iterate(s, (int)block_time);
1389
 
    }
1390
 
    if(ret != 0){
1391
 
      if (ret > 0 or errno != EINTR){
1392
 
        return (ret != 1) ? ret : 0;
1393
 
      }
1394
 
    }
1395
 
  }
1396
 
}
1397
 
 
1398
 
int main(int argc, char *argv[]){
1399
 
  AvahiSServiceBrowser *sb = NULL;
1400
 
  int error;
1401
 
  int ret;
1402
 
  intmax_t tmpmax;
1403
 
  char *tmp;
1404
 
  int exitcode = EXIT_SUCCESS;
1405
 
  const char *interface = "";
1406
 
  struct ifreq network;
1407
 
  int sd = -1;
1408
 
  bool take_down_interface = false;
1409
 
  uid_t uid;
1410
 
  gid_t gid;
1411
 
  char tempdir[] = "/tmp/mandosXXXXXX";
1412
 
  bool tempdir_created = false;
1413
 
  AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
1414
 
  const char *seckey = PATHDIR "/" SECKEY;
1415
 
  const char *pubkey = PATHDIR "/" PUBKEY;
1416
 
  
1417
 
  bool gnutls_initialized = false;
1418
 
  bool gpgme_initialized = false;
1419
 
  float delay = 2.5f;
1420
 
  double retry_interval = 10; /* 10s between trying a server and
1421
 
                                 retrying the same server again */
1422
 
  
1423
 
  struct sigaction old_sigterm_action = { .sa_handler = SIG_DFL };
1424
 
  struct sigaction sigterm_action = { .sa_handler = handle_sigterm };
1425
 
  
1426
 
  uid = getuid();
1427
 
  gid = getgid();
1428
 
  
1429
 
  /* Lower any group privileges we might have, just to be safe */
1430
 
  errno = 0;
1431
 
  ret = setgid(gid);
1432
 
  if(ret == -1){
1433
 
    perror_plus("setgid");
1434
 
  }
1435
 
  
1436
 
  /* Lower user privileges (temporarily) */
1437
 
  errno = 0;
1438
 
  ret = seteuid(uid);
1439
 
  if(ret == -1){
1440
 
    perror_plus("seteuid");
1441
 
  }
1442
 
  
1443
 
  if(quit_now){
1444
 
    goto end;
1445
 
  }
1446
 
  
1447
 
  {
1448
 
    struct argp_option options[] = {
1449
 
      { .name = "debug", .key = 128,
1450
 
        .doc = "Debug mode", .group = 3 },
1451
 
      { .name = "connect", .key = 'c',
1452
 
        .arg = "ADDRESS:PORT",
1453
 
        .doc = "Connect directly to a specific Mandos server",
1454
 
        .group = 1 },
1455
 
      { .name = "interface", .key = 'i',
1456
 
        .arg = "NAME",
1457
 
        .doc = "Network interface that will be used to search for"
1458
 
        " Mandos servers",
1459
 
        .group = 1 },
1460
 
      { .name = "seckey", .key = 's',
1461
 
        .arg = "FILE",
1462
 
        .doc = "OpenPGP secret key file base name",
1463
 
        .group = 1 },
1464
 
      { .name = "pubkey", .key = 'p',
1465
 
        .arg = "FILE",
1466
 
        .doc = "OpenPGP public key file base name",
1467
 
        .group = 2 },
1468
 
      { .name = "dh-bits", .key = 129,
1469
 
        .arg = "BITS",
1470
 
        .doc = "Bit length of the prime number used in the"
1471
 
        " Diffie-Hellman key exchange",
1472
 
        .group = 2 },
1473
 
      { .name = "priority", .key = 130,
1474
 
        .arg = "STRING",
1475
 
        .doc = "GnuTLS priority string for the TLS handshake",
1476
 
        .group = 1 },
1477
 
      { .name = "delay", .key = 131,
1478
 
        .arg = "SECONDS",
1479
 
        .doc = "Maximum delay to wait for interface startup",
1480
 
        .group = 2 },
1481
 
      { .name = "retry", .key = 132,
1482
 
        .arg = "SECONDS",
1483
 
        .doc = "Retry interval used when denied by the mandos server",
1484
 
        .group = 2 },
1485
 
      /*
1486
 
       * These reproduce what we would get without ARGP_NO_HELP
1487
 
       */
1488
 
      { .name = "help", .key = '?',
1489
 
        .doc = "Give this help list", .group = -1 },
1490
 
      { .name = "usage", .key = -3,
1491
 
        .doc = "Give a short usage message", .group = -1 },
1492
 
      { .name = "version", .key = 'V',
1493
 
        .doc = "Print program version", .group = -1 },
1494
 
      { .name = NULL }
1495
 
    };
 
648
/* Combines file name and path and returns the malloced new
 
649
   string. some sane checks could/should be added */
 
650
static const char *combinepath(const char *first, const char *second){
 
651
  size_t f_len = strlen(first);
 
652
  size_t s_len = strlen(second);
 
653
  char *tmp = malloc(f_len + s_len + 2);
 
654
  if (tmp == NULL){
 
655
    return NULL;
 
656
  }
 
657
  if(f_len > 0){
 
658
    memcpy(tmp, first, f_len);  /* Spurious warning */
 
659
  }
 
660
  tmp[f_len] = '/';
 
661
  if(s_len > 0){
 
662
    memcpy(tmp + f_len + 1, second, s_len); /* Spurious warning */
 
663
  }
 
664
  tmp[f_len + 1 + s_len] = '\0';
 
665
  return tmp;
 
666
}
 
667
 
 
668
 
 
669
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
 
670
    AvahiServerConfig config;
 
671
    AvahiSServiceBrowser *sb = NULL;
 
672
    int error;
 
673
    int ret;
 
674
    int debug_int;
 
675
    int returncode = EXIT_SUCCESS;
 
676
    const char *interface = "eth0";
 
677
    struct ifreq network;
 
678
    int sd;
 
679
    char *connect_to = NULL;
 
680
    AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
 
681
    mandos_context mc = { .simple_poll = NULL, .server = NULL,
 
682
                          .dh_bits = 1024, .priority = "SECURE256"};
1496
683
    
1497
 
    error_t parse_opt(int key, char *arg,
1498
 
                      struct argp_state *state){
1499
 
      errno = 0;
1500
 
      switch(key){
1501
 
      case 128:                 /* --debug */
1502
 
        debug = true;
1503
 
        break;
1504
 
      case 'c':                 /* --connect */
1505
 
        connect_to = arg;
1506
 
        break;
1507
 
      case 'i':                 /* --interface */
1508
 
        interface = arg;
1509
 
        break;
1510
 
      case 's':                 /* --seckey */
1511
 
        seckey = arg;
1512
 
        break;
1513
 
      case 'p':                 /* --pubkey */
1514
 
        pubkey = arg;
1515
 
        break;
1516
 
      case 129:                 /* --dh-bits */
1517
 
        errno = 0;
1518
 
        tmpmax = strtoimax(arg, &tmp, 10);
1519
 
        if(errno != 0 or tmp == arg or *tmp != '\0'
1520
 
           or tmpmax != (typeof(mc.dh_bits))tmpmax){
1521
 
          argp_error(state, "Bad number of DH bits");
1522
 
        }
1523
 
        mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
1524
 
        break;
1525
 
      case 130:                 /* --priority */
1526
 
        mc.priority = arg;
1527
 
        break;
1528
 
      case 131:                 /* --delay */
1529
 
        errno = 0;
1530
 
        delay = strtof(arg, &tmp);
1531
 
        if(errno != 0 or tmp == arg or *tmp != '\0'){
1532
 
          argp_error(state, "Bad delay");
1533
 
        }
1534
 
      case 132:                 /* --retry */
1535
 
        errno = 0;
1536
 
        retry_interval = strtod(arg, &tmp);
1537
 
        if(errno != 0 or tmp == arg or *tmp != '\0'
1538
 
           or (retry_interval * 1000) > INT_MAX
1539
 
           or retry_interval < 0){
1540
 
          argp_error(state, "Bad retry interval");
1541
 
        }
1542
 
        break;
1543
 
        /*
1544
 
         * These reproduce what we would get without ARGP_NO_HELP
1545
 
         */
1546
 
      case '?':                 /* --help */
1547
 
        argp_state_help(state, state->out_stream,
1548
 
                        (ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR)
1549
 
                        & ~(unsigned int)ARGP_HELP_EXIT_OK);
1550
 
      case -3:                  /* --usage */
1551
 
        argp_state_help(state, state->out_stream,
1552
 
                        ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
1553
 
      case 'V':                 /* --version */
1554
 
        fprintf(state->out_stream, "Mandos plugin mandos-client: ");
1555
 
        fprintf(state->out_stream, "%s\n", argp_program_version);
1556
 
        exit(argp_err_exit_status);
1557
 
        break;
 
684
    debug_int = debug ? 1 : 0;
 
685
    while (true){
 
686
      struct option long_options[] = {
 
687
        {"debug", no_argument, &debug_int, 1},
 
688
        {"connect", required_argument, NULL, 'c'},
 
689
        {"interface", required_argument, NULL, 'i'},
 
690
        {"keydir", required_argument, NULL, 'd'},
 
691
        {"seckey", required_argument, NULL, 's'},
 
692
        {"pubkey", required_argument, NULL, 'p'},
 
693
        {"dh-bits", required_argument, NULL, 'D'},
 
694
        {"priority", required_argument, NULL, 'P'},
 
695
        {0, 0, 0, 0} };
 
696
      
 
697
      int option_index = 0;
 
698
      ret = getopt_long (argc, argv, "i:", long_options,
 
699
                         &option_index);
 
700
      
 
701
      if (ret == -1){
 
702
        break;
 
703
      }
 
704
      
 
705
      switch(ret){
 
706
      case 0:
 
707
        break;
 
708
      case 'i':
 
709
        interface = optarg;
 
710
        break;
 
711
      case 'c':
 
712
        connect_to = optarg;
 
713
        break;
 
714
      case 'd':
 
715
        keydir = optarg;
 
716
        break;
 
717
      case 'p':
 
718
        pubkeyfile = optarg;
 
719
        break;
 
720
      case 's':
 
721
        seckeyfile = optarg;
 
722
        break;
 
723
      case 'D':
 
724
        errno = 0;
 
725
        mc.dh_bits = (unsigned int) strtol(optarg, NULL, 10);
 
726
        if (errno){
 
727
          perror("strtol");
 
728
          exit(EXIT_FAILURE);
 
729
        }
 
730
        break;
 
731
      case 'P':
 
732
        mc.priority = optarg;
 
733
        break;
 
734
      case '?':
1558
735
      default:
1559
 
        return ARGP_ERR_UNKNOWN;
1560
 
      }
1561
 
      return errno;
1562
 
    }
1563
 
    
1564
 
    struct argp argp = { .options = options, .parser = parse_opt,
1565
 
                         .args_doc = "",
1566
 
                         .doc = "Mandos client -- Get and decrypt"
1567
 
                         " passwords from a Mandos server" };
1568
 
    ret = argp_parse(&argp, argc, argv,
1569
 
                     ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
1570
 
    switch(ret){
1571
 
    case 0:
1572
 
      break;
1573
 
    case ENOMEM:
1574
 
    default:
1575
 
      errno = ret;
1576
 
      perror_plus("argp_parse");
1577
 
      exitcode = EX_OSERR;
1578
 
      goto end;
1579
 
    case EINVAL:
1580
 
      exitcode = EX_USAGE;
1581
 
      goto end;
1582
 
    }
1583
 
  }
1584
 
    
1585
 
  {
1586
 
    /* Work around Debian bug #633582:
1587
 
       <http://bugs.debian.org/633582> */
1588
 
    struct stat st;
1589
 
    
1590
 
    /* Re-raise priviliges */
1591
 
    errno = 0;
1592
 
    ret = seteuid(0);
1593
 
    if(ret == -1){
1594
 
      perror_plus("seteuid");
1595
 
    }
1596
 
    
1597
 
    if(strcmp(seckey, PATHDIR "/" SECKEY) == 0){
1598
 
      int seckey_fd = open(seckey, O_RDONLY);
1599
 
      if(seckey_fd == -1){
1600
 
        perror_plus("open");
1601
 
      } else {
1602
 
        ret = (int)TEMP_FAILURE_RETRY(fstat(seckey_fd, &st));
1603
 
        if(ret == -1){
1604
 
          perror_plus("fstat");
1605
 
        } else {
1606
 
          if(S_ISREG(st.st_mode) and st.st_uid == 0 and st.st_gid == 0){
1607
 
            ret = fchown(seckey_fd, uid, gid);
1608
 
            if(ret == -1){
1609
 
              perror_plus("fchown");
1610
 
            }
1611
 
          }
1612
 
        }
1613
 
        TEMP_FAILURE_RETRY(close(seckey_fd));
1614
 
      }
1615
 
    }
1616
 
    
1617
 
    if(strcmp(pubkey, PATHDIR "/" PUBKEY) == 0){
1618
 
      int pubkey_fd = open(pubkey, O_RDONLY);
1619
 
      if(pubkey_fd == -1){
1620
 
        perror_plus("open");
1621
 
      } else {
1622
 
        ret = (int)TEMP_FAILURE_RETRY(fstat(pubkey_fd, &st));
1623
 
        if(ret == -1){
1624
 
          perror_plus("fstat");
1625
 
        } else {
1626
 
          if(S_ISREG(st.st_mode) and st.st_uid == 0 and st.st_gid == 0){
1627
 
            ret = fchown(pubkey_fd, uid, gid);
1628
 
            if(ret == -1){
1629
 
              perror_plus("fchown");
1630
 
            }
1631
 
          }
1632
 
        }
1633
 
        TEMP_FAILURE_RETRY(close(pubkey_fd));
1634
 
      }
1635
 
    }
1636
 
    
1637
 
    /* Lower privileges */
1638
 
    errno = 0;
1639
 
    ret = seteuid(uid);
1640
 
    if(ret == -1){
1641
 
      perror_plus("seteuid");
1642
 
    }
1643
 
  }
1644
 
  
1645
 
  /* Find network hooks and run them */
1646
 
  {
1647
 
    struct dirent **direntries;
1648
 
    struct dirent *direntry;
1649
 
    int numhooks = scandir(HOOKDIR, &direntries, runnable_hook,
1650
 
                           alphasort);
1651
 
    if(numhooks == -1){
1652
 
      perror_plus("scandir");
1653
 
    } else {
1654
 
      int devnull = open("/dev/null", O_RDONLY);
1655
 
      for(int i = 0; i < numhooks; i++){
1656
 
        direntry = direntries[0];
1657
 
        char *fullname = NULL;
1658
 
        ret = asprintf(&fullname, "%s/%s", tempdir,
1659
 
                       direntry->d_name);
1660
 
        if(ret < 0){
1661
 
          perror_plus("asprintf");
1662
 
          continue;
1663
 
        }
1664
 
        pid_t hook_pid = fork();
1665
 
        if(hook_pid == 0){
1666
 
          /* Child */
1667
 
          dup2(devnull, STDIN_FILENO);
1668
 
          close(devnull);
1669
 
          dup2(STDERR_FILENO, STDOUT_FILENO);
1670
 
          ret = setenv("DEVICE", interface, 1);
1671
 
          if(ret == -1){
1672
 
            perror_plus("setenv");
1673
 
            exit(1);
1674
 
          }
1675
 
          ret = setenv("VERBOSE", debug ? "1" : "0", 1);
1676
 
          if(ret == -1){
1677
 
            perror_plus("setenv");
1678
 
            exit(1);
1679
 
          }
1680
 
          ret = setenv("MODE", "start", 1);
1681
 
          if(ret == -1){
1682
 
            perror_plus("setenv");
1683
 
            exit(1);
1684
 
          }
1685
 
          char *delaystring;
1686
 
          ret = asprintf(&delaystring, "%f", delay);
1687
 
          if(ret == -1){
1688
 
            perror_plus("asprintf");
1689
 
            exit(1);
1690
 
          }
1691
 
          ret = setenv("DELAY", delaystring, 1);
1692
 
          if(ret == -1){
1693
 
            free(delaystring);
1694
 
            perror_plus("setenv");
1695
 
            exit(1);
1696
 
          }
1697
 
          free(delaystring);
1698
 
          ret = execl(fullname, direntry->d_name, "start", NULL);
1699
 
          perror_plus("execl");
1700
 
        } else {
1701
 
          int status;
1702
 
          if(TEMP_FAILURE_RETRY(waitpid(hook_pid, &status, 0)) == -1){
1703
 
            perror_plus("waitpid");
1704
 
            free(fullname);
1705
 
            continue;
1706
 
          }
1707
 
          if(WIFEXITED(status)){
1708
 
            if(WEXITSTATUS(status) != 0){
1709
 
              fprintf(stderr, "Mandos plugin mandos-client: "
1710
 
                      "Warning: network hook \"%s\" exited"
1711
 
                      " with status %d\n", direntry->d_name,
1712
 
                      WEXITSTATUS(status));
1713
 
              free(fullname);
1714
 
              continue;
1715
 
            }
1716
 
          } else if(WIFSIGNALED(status)){
1717
 
            fprintf(stderr, "Mandos plugin mandos-client: "
1718
 
                    "Warning: network hook \"%s\" died by"
1719
 
                    " signal %d\n", direntry->d_name,
1720
 
                    WTERMSIG(status));
1721
 
            free(fullname);
1722
 
            continue;
1723
 
          } else {
1724
 
            fprintf(stderr, "Mandos plugin mandos-client: "
1725
 
                    "Warning: network hook \"%s\" crashed\n",
1726
 
                    direntry->d_name);
1727
 
            free(fullname);
1728
 
            continue;
1729
 
          }
1730
 
        }
1731
 
        free(fullname);
1732
 
        if(quit_now){
1733
 
          goto end;
1734
 
        }
1735
 
      }
1736
 
      close(devnull);
1737
 
    }
1738
 
  }
1739
 
  
1740
 
  if(not debug){
1741
 
    avahi_set_log_function(empty_log);
1742
 
  }
1743
 
  
1744
 
  if(interface[0] == '\0'){
1745
 
    struct dirent **direntries;
1746
 
    /* First look for interfaces that are up */
1747
 
    ret = scandir(sys_class_net, &direntries, up_interface,
1748
 
                  alphasort);
1749
 
    if(ret == 0){
1750
 
      /* No up interfaces, look for any good interfaces */
1751
 
      free(direntries);
1752
 
      ret = scandir(sys_class_net, &direntries, good_interface,
1753
 
                    alphasort);
1754
 
    }
1755
 
    if(ret >= 1){
1756
 
      /* Pick the first interface returned */
1757
 
      interface = strdup(direntries[0]->d_name);
1758
 
      if(debug){
1759
 
        fprintf(stderr, "Mandos plugin mandos-client: "
1760
 
                "Using interface \"%s\"\n", interface);
1761
 
      }
1762
 
      if(interface == NULL){
1763
 
        perror_plus("malloc");
1764
 
        free(direntries);
1765
 
        exitcode = EXIT_FAILURE;
1766
 
        goto end;
1767
 
      }
1768
 
      free(direntries);
1769
 
    } else {
1770
 
      free(direntries);
1771
 
      fprintf(stderr, "Mandos plugin mandos-client: "
1772
 
              "Could not find a network interface\n");
1773
 
      exitcode = EXIT_FAILURE;
1774
 
      goto end;
1775
 
    }
1776
 
  }
1777
 
  
1778
 
  /* Initialize Avahi early so avahi_simple_poll_quit() can be called
1779
 
     from the signal handler */
1780
 
  /* Initialize the pseudo-RNG for Avahi */
1781
 
  srand((unsigned int) time(NULL));
1782
 
  mc.simple_poll = avahi_simple_poll_new();
1783
 
  if(mc.simple_poll == NULL){
1784
 
    fprintf(stderr, "Mandos plugin mandos-client: "
1785
 
            "Avahi: Failed to create simple poll object.\n");
1786
 
    exitcode = EX_UNAVAILABLE;
1787
 
    goto end;
1788
 
  }
1789
 
  
1790
 
  sigemptyset(&sigterm_action.sa_mask);
1791
 
  ret = sigaddset(&sigterm_action.sa_mask, SIGINT);
1792
 
  if(ret == -1){
1793
 
    perror_plus("sigaddset");
1794
 
    exitcode = EX_OSERR;
1795
 
    goto end;
1796
 
  }
1797
 
  ret = sigaddset(&sigterm_action.sa_mask, SIGHUP);
1798
 
  if(ret == -1){
1799
 
    perror_plus("sigaddset");
1800
 
    exitcode = EX_OSERR;
1801
 
    goto end;
1802
 
  }
1803
 
  ret = sigaddset(&sigterm_action.sa_mask, SIGTERM);
1804
 
  if(ret == -1){
1805
 
    perror_plus("sigaddset");
1806
 
    exitcode = EX_OSERR;
1807
 
    goto end;
1808
 
  }
1809
 
  /* Need to check if the handler is SIG_IGN before handling:
1810
 
     | [[info:libc:Initial Signal Actions]] |
1811
 
     | [[info:libc:Basic Signal Handling]]  |
1812
 
  */
1813
 
  ret = sigaction(SIGINT, NULL, &old_sigterm_action);
1814
 
  if(ret == -1){
1815
 
    perror_plus("sigaction");
1816
 
    return EX_OSERR;
1817
 
  }
1818
 
  if(old_sigterm_action.sa_handler != SIG_IGN){
1819
 
    ret = sigaction(SIGINT, &sigterm_action, NULL);
1820
 
    if(ret == -1){
1821
 
      perror_plus("sigaction");
1822
 
      exitcode = EX_OSERR;
1823
 
      goto end;
1824
 
    }
1825
 
  }
1826
 
  ret = sigaction(SIGHUP, NULL, &old_sigterm_action);
1827
 
  if(ret == -1){
1828
 
    perror_plus("sigaction");
1829
 
    return EX_OSERR;
1830
 
  }
1831
 
  if(old_sigterm_action.sa_handler != SIG_IGN){
1832
 
    ret = sigaction(SIGHUP, &sigterm_action, NULL);
1833
 
    if(ret == -1){
1834
 
      perror_plus("sigaction");
1835
 
      exitcode = EX_OSERR;
1836
 
      goto end;
1837
 
    }
1838
 
  }
1839
 
  ret = sigaction(SIGTERM, NULL, &old_sigterm_action);
1840
 
  if(ret == -1){
1841
 
    perror_plus("sigaction");
1842
 
    return EX_OSERR;
1843
 
  }
1844
 
  if(old_sigterm_action.sa_handler != SIG_IGN){
1845
 
    ret = sigaction(SIGTERM, &sigterm_action, NULL);
1846
 
    if(ret == -1){
1847
 
      perror_plus("sigaction");
1848
 
      exitcode = EX_OSERR;
1849
 
      goto end;
1850
 
    }
1851
 
  }
1852
 
  
1853
 
  /* If the interface is down, bring it up */
1854
 
  if(strcmp(interface, "none") != 0){
 
736
        exit(EXIT_FAILURE);
 
737
      }
 
738
    }
 
739
    debug = debug_int ? true : false;
 
740
    
 
741
    pubkeyfile = combinepath(keydir, pubkeyfile);
 
742
    if (pubkeyfile == NULL){
 
743
      perror("combinepath");
 
744
      returncode = EXIT_FAILURE;
 
745
      goto exit;
 
746
    }
 
747
    
 
748
    seckeyfile = combinepath(keydir, seckeyfile);
 
749
    if (seckeyfile == NULL){
 
750
      perror("combinepath");
 
751
      goto exit;
 
752
    }
 
753
    
1855
754
    if_index = (AvahiIfIndex) if_nametoindex(interface);
1856
755
    if(if_index == 0){
1857
 
      fprintf(stderr, "Mandos plugin mandos-client: "
1858
 
              "No such interface: \"%s\"\n", interface);
1859
 
      exitcode = EX_UNAVAILABLE;
1860
 
      goto end;
1861
 
    }
1862
 
    
1863
 
    if(quit_now){
1864
 
      goto end;
1865
 
    }
1866
 
    
1867
 
    /* Re-raise priviliges */
1868
 
    errno = 0;
1869
 
    ret = seteuid(0);
1870
 
    if(ret == -1){
1871
 
      perror_plus("seteuid");
1872
 
    }
1873
 
    
1874
 
#ifdef __linux__
1875
 
    /* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
1876
 
       messages about the network interface to mess up the prompt */
1877
 
    ret = klogctl(8, NULL, 5);
1878
 
    bool restore_loglevel = true;
1879
 
    if(ret == -1){
1880
 
      restore_loglevel = false;
1881
 
      perror_plus("klogctl");
1882
 
    }
1883
 
#endif  /* __linux__ */
 
756
      fprintf(stderr, "No such interface: \"%s\"\n", interface);
 
757
      exit(EXIT_FAILURE);
 
758
    }
 
759
    
 
760
    if(connect_to != NULL){
 
761
      /* Connect directly, do not use Zeroconf */
 
762
      /* (Mainly meant for debugging) */
 
763
      char *address = strrchr(connect_to, ':');
 
764
      if(address == NULL){
 
765
        fprintf(stderr, "No colon in address\n");
 
766
        exit(EXIT_FAILURE);
 
767
      }
 
768
      errno = 0;
 
769
      uint16_t port = (uint16_t) strtol(address+1, NULL, 10);
 
770
      if(errno){
 
771
        perror("Bad port number");
 
772
        exit(EXIT_FAILURE);
 
773
      }
 
774
      *address = '\0';
 
775
      address = connect_to;
 
776
      ret = start_mandos_communication(address, port, if_index, &mc);
 
777
      if(ret < 0){
 
778
        exit(EXIT_FAILURE);
 
779
      } else {
 
780
        exit(EXIT_SUCCESS);
 
781
      }
 
782
    }
1884
783
    
1885
784
    sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1886
 
    if(sd < 0){
1887
 
      perror_plus("socket");
1888
 
      exitcode = EX_OSERR;
1889
 
#ifdef __linux__
1890
 
      if(restore_loglevel){
1891
 
        ret = klogctl(7, NULL, 0);
1892
 
        if(ret == -1){
1893
 
          perror_plus("klogctl");
1894
 
        }
1895
 
      }
1896
 
#endif  /* __linux__ */
1897
 
      /* Lower privileges */
1898
 
      errno = 0;
1899
 
      ret = seteuid(uid);
1900
 
      if(ret == -1){
1901
 
        perror_plus("seteuid");
1902
 
      }
1903
 
      goto end;
 
785
    if(sd < 0) {
 
786
      perror("socket");
 
787
      returncode = EXIT_FAILURE;
 
788
      goto exit;
1904
789
    }
1905
 
    strcpy(network.ifr_name, interface);
 
790
    strcpy(network.ifr_name, interface); /* Spurious warning */
1906
791
    ret = ioctl(sd, SIOCGIFFLAGS, &network);
1907
792
    if(ret == -1){
1908
 
      perror_plus("ioctl SIOCGIFFLAGS");
1909
 
#ifdef __linux__
1910
 
      if(restore_loglevel){
1911
 
        ret = klogctl(7, NULL, 0);
1912
 
        if(ret == -1){
1913
 
          perror_plus("klogctl");
1914
 
        }
1915
 
      }
1916
 
#endif  /* __linux__ */
1917
 
      exitcode = EX_OSERR;
1918
 
      /* Lower privileges */
1919
 
      errno = 0;
1920
 
      ret = seteuid(uid);
1921
 
      if(ret == -1){
1922
 
        perror_plus("seteuid");
1923
 
      }
1924
 
      goto end;
 
793
      
 
794
      perror("ioctl SIOCGIFFLAGS");
 
795
      returncode = EXIT_FAILURE;
 
796
      goto exit;
1925
797
    }
1926
798
    if((network.ifr_flags & IFF_UP) == 0){
1927
799
      network.ifr_flags |= IFF_UP;
1928
 
      take_down_interface = true;
1929
800
      ret = ioctl(sd, SIOCSIFFLAGS, &network);
1930
801
      if(ret == -1){
1931
 
        take_down_interface = false;
1932
 
        perror_plus("ioctl SIOCSIFFLAGS +IFF_UP");
1933
 
        exitcode = EX_OSERR;
1934
 
#ifdef __linux__
1935
 
        if(restore_loglevel){
1936
 
          ret = klogctl(7, NULL, 0);
1937
 
          if(ret == -1){
1938
 
            perror_plus("klogctl");
1939
 
          }
1940
 
        }
1941
 
#endif  /* __linux__ */
1942
 
        /* Lower privileges */
1943
 
        errno = 0;
1944
 
        ret = seteuid(uid);
1945
 
        if(ret == -1){
1946
 
          perror_plus("seteuid");
1947
 
        }
1948
 
        goto end;
1949
 
      }
1950
 
    }
1951
 
    /* Sleep checking until interface is running.
1952
 
       Check every 0.25s, up to total time of delay */
1953
 
    for(int i=0; i < delay * 4; i++){
1954
 
      ret = ioctl(sd, SIOCGIFFLAGS, &network);
1955
 
      if(ret == -1){
1956
 
        perror_plus("ioctl SIOCGIFFLAGS");
1957
 
      } else if(network.ifr_flags & IFF_RUNNING){
1958
 
        break;
1959
 
      }
1960
 
      struct timespec sleeptime = { .tv_nsec = 250000000 };
1961
 
      ret = nanosleep(&sleeptime, NULL);
1962
 
      if(ret == -1 and errno != EINTR){
1963
 
        perror_plus("nanosleep");
1964
 
      }
1965
 
    }
1966
 
    if(not take_down_interface){
1967
 
      /* We won't need the socket anymore */
1968
 
      ret = (int)TEMP_FAILURE_RETRY(close(sd));
1969
 
      if(ret == -1){
1970
 
        perror_plus("close");
1971
 
      }
1972
 
    }
1973
 
#ifdef __linux__
1974
 
    if(restore_loglevel){
1975
 
      /* Restores kernel loglevel to default */
1976
 
      ret = klogctl(7, NULL, 0);
1977
 
      if(ret == -1){
1978
 
        perror_plus("klogctl");
1979
 
      }
1980
 
    }
1981
 
#endif  /* __linux__ */
1982
 
    /* Lower privileges */
1983
 
    errno = 0;
1984
 
    if(take_down_interface){
1985
 
      /* Lower privileges */
1986
 
      ret = seteuid(uid);
1987
 
      if(ret == -1){
1988
 
        perror_plus("seteuid");
1989
 
      }
1990
 
    } else {
1991
 
      /* Lower privileges permanently */
1992
 
      ret = setuid(uid);
1993
 
      if(ret == -1){
1994
 
        perror_plus("setuid");
1995
 
      }
1996
 
    }
1997
 
  }
1998
 
  
1999
 
  if(quit_now){
2000
 
    goto end;
2001
 
  }
2002
 
  
2003
 
  ret = init_gnutls_global(pubkey, seckey);
2004
 
  if(ret == -1){
2005
 
    fprintf(stderr, "Mandos plugin mandos-client: "
2006
 
            "init_gnutls_global failed\n");
2007
 
    exitcode = EX_UNAVAILABLE;
2008
 
    goto end;
2009
 
  } else {
2010
 
    gnutls_initialized = true;
2011
 
  }
2012
 
  
2013
 
  if(quit_now){
2014
 
    goto end;
2015
 
  }
2016
 
  
2017
 
  if(mkdtemp(tempdir) == NULL){
2018
 
    perror_plus("mkdtemp");
2019
 
    goto end;
2020
 
  }
2021
 
  tempdir_created = true;
2022
 
  
2023
 
  if(quit_now){
2024
 
    goto end;
2025
 
  }
2026
 
  
2027
 
  if(not init_gpgme(pubkey, seckey, tempdir)){
2028
 
    fprintf(stderr, "Mandos plugin mandos-client: "
2029
 
            "init_gpgme failed\n");
2030
 
    exitcode = EX_UNAVAILABLE;
2031
 
    goto end;
2032
 
  } else {
2033
 
    gpgme_initialized = true;
2034
 
  }
2035
 
  
2036
 
  if(quit_now){
2037
 
    goto end;
2038
 
  }
2039
 
  
2040
 
  if(connect_to != NULL){
2041
 
    /* Connect directly, do not use Zeroconf */
2042
 
    /* (Mainly meant for debugging) */
2043
 
    char *address = strrchr(connect_to, ':');
2044
 
    if(address == NULL){
2045
 
      fprintf(stderr, "Mandos plugin mandos-client: "
2046
 
              "No colon in address\n");
2047
 
      exitcode = EX_USAGE;
2048
 
      goto end;
2049
 
    }
2050
 
    
2051
 
    if(quit_now){
2052
 
      goto end;
2053
 
    }
2054
 
    
2055
 
    uint16_t port;
2056
 
    errno = 0;
2057
 
    tmpmax = strtoimax(address+1, &tmp, 10);
2058
 
    if(errno != 0 or tmp == address+1 or *tmp != '\0'
2059
 
       or tmpmax != (uint16_t)tmpmax){
2060
 
      fprintf(stderr, "Mandos plugin mandos-client: "
2061
 
              "Bad port number\n");
2062
 
      exitcode = EX_USAGE;
2063
 
      goto end;
2064
 
    }
2065
 
  
2066
 
    if(quit_now){
2067
 
      goto end;
2068
 
    }
2069
 
    
2070
 
    port = (uint16_t)tmpmax;
2071
 
    *address = '\0';
2072
 
    /* Colon in address indicates IPv6 */
2073
 
    int af;
2074
 
    if(strchr(connect_to, ':') != NULL){
2075
 
      af = AF_INET6;
2076
 
      /* Accept [] around IPv6 address - see RFC 5952 */
2077
 
      if(connect_to[0] == '[' and address[-1] == ']')
2078
 
        {
2079
 
          connect_to++;
2080
 
          address[-1] = '\0';
2081
 
        }
2082
 
    } else {
2083
 
      af = AF_INET;
2084
 
    }
2085
 
    address = connect_to;
2086
 
    
2087
 
    if(quit_now){
2088
 
      goto end;
2089
 
    }
2090
 
    
2091
 
    while(not quit_now){
2092
 
      ret = start_mandos_communication(address, port, if_index, af);
2093
 
      if(quit_now or ret == 0){
2094
 
        break;
2095
 
      }
2096
 
      if(debug){
2097
 
        fprintf(stderr, "Mandos plugin mandos-client: "
2098
 
                "Retrying in %d seconds\n", (int)retry_interval);
2099
 
      }
2100
 
      sleep((int)retry_interval);
2101
 
    }
2102
 
    
2103
 
    if (not quit_now){
2104
 
      exitcode = EXIT_SUCCESS;
2105
 
    }
2106
 
    
2107
 
    goto end;
2108
 
  }
2109
 
  
2110
 
  if(quit_now){
2111
 
    goto end;
2112
 
  }
2113
 
  
2114
 
  {
2115
 
    AvahiServerConfig config;
2116
 
    /* Do not publish any local Zeroconf records */
 
802
        perror("ioctl SIOCSIFFLAGS");
 
803
        returncode = EXIT_FAILURE;
 
804
        goto exit;
 
805
      }
 
806
    }
 
807
    close(sd);
 
808
    
 
809
    if (not debug){
 
810
      avahi_set_log_function(empty_log);
 
811
    }
 
812
    
 
813
    /* Initialize the psuedo-RNG */
 
814
    srand((unsigned int) time(NULL));
 
815
 
 
816
    /* Allocate main loop object */
 
817
    if (!(mc.simple_poll = avahi_simple_poll_new())) {
 
818
        fprintf(stderr, "Failed to create simple poll object.\n");
 
819
        returncode = EXIT_FAILURE;
 
820
        goto exit;
 
821
    }
 
822
 
 
823
    /* Do not publish any local records */
2117
824
    avahi_server_config_init(&config);
2118
825
    config.publish_hinfo = 0;
2119
826
    config.publish_addresses = 0;
2120
827
    config.publish_workstation = 0;
2121
828
    config.publish_domain = 0;
2122
 
    
 
829
 
2123
830
    /* Allocate a new server */
2124
 
    mc.server = avahi_server_new(avahi_simple_poll_get
2125
 
                                 (mc.simple_poll), &config, NULL,
2126
 
                                 NULL, &error);
 
831
    mc.server=avahi_server_new(avahi_simple_poll_get(mc.simple_poll),
 
832
                               &config, NULL, NULL, &error);
2127
833
    
2128
 
    /* Free the Avahi configuration data */
 
834
    /* Free the configuration data */
2129
835
    avahi_server_config_free(&config);
2130
 
  }
2131
 
  
2132
 
  /* Check if creating the Avahi server object succeeded */
2133
 
  if(mc.server == NULL){
2134
 
    fprintf(stderr, "Mandos plugin mandos-client: "
2135
 
            "Failed to create Avahi server: %s\n",
2136
 
            avahi_strerror(error));
2137
 
    exitcode = EX_UNAVAILABLE;
2138
 
    goto end;
2139
 
  }
2140
 
  
2141
 
  if(quit_now){
2142
 
    goto end;
2143
 
  }
2144
 
  
2145
 
  /* Create the Avahi service browser */
2146
 
  sb = avahi_s_service_browser_new(mc.server, if_index,
2147
 
                                   AVAHI_PROTO_UNSPEC, "_mandos._tcp",
2148
 
                                   NULL, 0, browse_callback, NULL);
2149
 
  if(sb == NULL){
2150
 
    fprintf(stderr, "Mandos plugin mandos-client: "
2151
 
            "Failed to create service browser: %s\n",
2152
 
            avahi_strerror(avahi_server_errno(mc.server)));
2153
 
    exitcode = EX_UNAVAILABLE;
2154
 
    goto end;
2155
 
  }
2156
 
  
2157
 
  if(quit_now){
2158
 
    goto end;
2159
 
  }
2160
 
  
2161
 
  /* Run the main loop */
2162
 
  
2163
 
  if(debug){
2164
 
    fprintf(stderr, "Mandos plugin mandos-client: "
2165
 
            "Starting Avahi loop search\n");
2166
 
  }
2167
 
 
2168
 
  ret = avahi_loop_with_timeout(mc.simple_poll,
2169
 
                                (int)(retry_interval * 1000));
2170
 
  if(debug){
2171
 
    fprintf(stderr, "Mandos plugin mandos-client: "
2172
 
            "avahi_loop_with_timeout exited %s\n",
2173
 
            (ret == 0) ? "successfully" : "with error");
2174
 
  }
2175
 
  
2176
 
 end:
2177
 
  
2178
 
  if(debug){
2179
 
    fprintf(stderr, "Mandos plugin mandos-client: "
2180
 
            "%s exiting\n", argv[0]);
2181
 
  }
2182
 
  
2183
 
  /* Cleanup things */
2184
 
  if(sb != NULL)
2185
 
    avahi_s_service_browser_free(sb);
2186
 
  
2187
 
  if(mc.server != NULL)
2188
 
    avahi_server_free(mc.server);
2189
 
  
2190
 
  if(mc.simple_poll != NULL)
2191
 
    avahi_simple_poll_free(mc.simple_poll);
2192
 
  
2193
 
  if(gnutls_initialized){
2194
 
    gnutls_certificate_free_credentials(mc.cred);
2195
 
    gnutls_global_deinit();
2196
 
    gnutls_dh_params_deinit(mc.dh_params);
2197
 
  }
2198
 
  
2199
 
  if(gpgme_initialized){
2200
 
    gpgme_release(mc.ctx);
2201
 
  }
2202
 
 
2203
 
  /* Cleans up the circular linked list of Mandos servers the client
2204
 
     has seen */
2205
 
  if(mc.current_server != NULL){
2206
 
    mc.current_server->prev->next = NULL;
2207
 
    while(mc.current_server != NULL){
2208
 
      server *next = mc.current_server->next;
2209
 
      free(mc.current_server);
2210
 
      mc.current_server = next;
2211
 
    }
2212
 
  }
2213
 
  
2214
 
  /* XXX run network hooks "stop" here  */
2215
 
  
2216
 
  /* Take down the network interface */
2217
 
  if(take_down_interface){
2218
 
    /* Re-raise priviliges */
2219
 
    errno = 0;
2220
 
    ret = seteuid(0);
2221
 
    if(ret == -1){
2222
 
      perror_plus("seteuid");
2223
 
    }
2224
 
    if(geteuid() == 0){
2225
 
      ret = ioctl(sd, SIOCGIFFLAGS, &network);
2226
 
      if(ret == -1){
2227
 
        perror_plus("ioctl SIOCGIFFLAGS");
2228
 
      } else if(network.ifr_flags & IFF_UP){
2229
 
        network.ifr_flags &= ~(short)IFF_UP; /* clear flag */
2230
 
        ret = ioctl(sd, SIOCSIFFLAGS, &network);
2231
 
        if(ret == -1){
2232
 
          perror_plus("ioctl SIOCSIFFLAGS -IFF_UP");
2233
 
        }
2234
 
      }
2235
 
      ret = (int)TEMP_FAILURE_RETRY(close(sd));
2236
 
      if(ret == -1){
2237
 
        perror_plus("close");
2238
 
      }
2239
 
      /* Lower privileges permanently */
2240
 
      errno = 0;
2241
 
      ret = setuid(uid);
2242
 
      if(ret == -1){
2243
 
        perror_plus("setuid");
2244
 
      }
2245
 
    }
2246
 
  }
2247
 
  
2248
 
  /* Removes the GPGME temp directory and all files inside */
2249
 
  if(tempdir_created){
2250
 
    struct dirent **direntries = NULL;
2251
 
    struct dirent *direntry = NULL;
2252
 
    int numentries = scandir(tempdir, &direntries, notdotentries,
2253
 
                             alphasort);
2254
 
    if (numentries > 0){
2255
 
      for(int i = 0; i < numentries; i++){
2256
 
        direntry = direntries[i];
2257
 
        char *fullname = NULL;
2258
 
        ret = asprintf(&fullname, "%s/%s", tempdir,
2259
 
                       direntry->d_name);
2260
 
        if(ret < 0){
2261
 
          perror_plus("asprintf");
2262
 
          continue;
2263
 
        }
2264
 
        ret = remove(fullname);
2265
 
        if(ret == -1){
2266
 
          fprintf(stderr, "Mandos plugin mandos-client: "
2267
 
                  "remove(\"%s\"): %s\n", fullname, strerror(errno));
2268
 
        }
2269
 
        free(fullname);
2270
 
      }
2271
 
    }
2272
 
 
2273
 
    /* need to clean even if 0 because man page doesn't specify */
2274
 
    free(direntries);
2275
 
    if (numentries == -1){
2276
 
      perror_plus("scandir");
2277
 
    }
2278
 
    ret = rmdir(tempdir);
2279
 
    if(ret == -1 and errno != ENOENT){
2280
 
      perror_plus("rmdir");
2281
 
    }
2282
 
  }
2283
 
  
2284
 
  if(quit_now){
2285
 
    sigemptyset(&old_sigterm_action.sa_mask);
2286
 
    old_sigterm_action.sa_handler = SIG_DFL;
2287
 
    ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received,
2288
 
                                            &old_sigterm_action,
2289
 
                                            NULL));
2290
 
    if(ret == -1){
2291
 
      perror_plus("sigaction");
2292
 
    }
2293
 
    do {
2294
 
      ret = raise(signal_received);
2295
 
    } while(ret != 0 and errno == EINTR);
2296
 
    if(ret != 0){
2297
 
      perror_plus("raise");
2298
 
      abort();
2299
 
    }
2300
 
    TEMP_FAILURE_RETRY(pause());
2301
 
  }
2302
 
  
2303
 
  return exitcode;
 
836
    
 
837
    /* Check if creating the server object succeeded */
 
838
    if (!mc.server) {
 
839
        fprintf(stderr, "Failed to create server: %s\n",
 
840
                avahi_strerror(error));
 
841
        returncode = EXIT_FAILURE;
 
842
        goto exit;
 
843
    }
 
844
    
 
845
    /* Create the service browser */
 
846
    sb = avahi_s_service_browser_new(mc.server, if_index,
 
847
                                     AVAHI_PROTO_INET6,
 
848
                                     "_mandos._tcp", NULL, 0,
 
849
                                     browse_callback, &mc);
 
850
    if (!sb) {
 
851
        fprintf(stderr, "Failed to create service browser: %s\n",
 
852
                avahi_strerror(avahi_server_errno(mc.server)));
 
853
        returncode = EXIT_FAILURE;
 
854
        goto exit;
 
855
    }
 
856
    
 
857
    /* Run the main loop */
 
858
 
 
859
    if (debug){
 
860
      fprintf(stderr, "Starting avahi loop search\n");
 
861
    }
 
862
    
 
863
    avahi_simple_poll_loop(mc.simple_poll);
 
864
    
 
865
 exit:
 
866
 
 
867
    if (debug){
 
868
      fprintf(stderr, "%s exiting\n", argv[0]);
 
869
    }
 
870
    
 
871
    /* Cleanup things */
 
872
    if (sb)
 
873
        avahi_s_service_browser_free(sb);
 
874
    
 
875
    if (mc.server)
 
876
        avahi_server_free(mc.server);
 
877
 
 
878
    if (mc.simple_poll)
 
879
        avahi_simple_poll_free(mc.simple_poll);
 
880
    free(pubkeyfile);
 
881
    free(seckeyfile);
 
882
    
 
883
    return returncode;
2304
884
}