/mandos/trunk

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

« back to all changes in this revision

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

merge

Show diffs side-by-side

added added

removed removed

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