/mandos/release

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

« back to all changes in this revision

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

  • Committer: Björn Påhlsson
  • Date: 2011-06-18 22:51:12 UTC
  • mto: (237.7.33 trunk)
  • mto: This revision was merged to the branch mainline in revision 284.
  • Revision ID: belorn@fukt.bsnet.se-20110618225112-rnt2m4ek32758f80
using scandir instead of readdir

Show diffs side-by-side

added added

removed removed

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