/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

First version of a somewhat complete D-Bus server interface.  Also
change user/group name to "_mandos".

* debian/mandos.postinst: Rename old "mandos" user and group to
                          "_mandos"; create "_mandos" user and group
                          if none exist.
* debian/mandos-client.postinst: - '' -

* initramfs-tools-hook: Try "_mandos" before "mandos" as user and
                        group name.

* mandos (_datetime_to_dbus_struct): New; was previously local.
  (Client.started): Renamed to "last_started".  All users changed.
  (Client.started): New; boolean.
  (Client.dbus_object_path): New.
  (Client.check_command): Renamed to "checker_command".  All users
                          changed.
  (Client.__init__): Set and use "self.dbus_object_path".  Set
                     "self.started".
  (Client.start): Update "self.started".  Emit "self.PropertyChanged"
                  signals for both "started" and "last_started".
  (Client.stop): Update "self.started".  Emit "self.PropertyChanged"
                 signal for "started".
  (Client.checker_callback): Take additional "command" argument.  All
                             callers changed. Emit
                             "self.PropertyChanged" signal.
  (Client.bump_timeout): Emit "self.PropertyChanged" signal for
                         "last_checked_ok".
  (Client.start_checker): Emit "self.PropertyChanged" signal for
                          "checker_running".
  (Client.stop_checker): Emit "self.PropertyChanged" signal for
                         "checker_running".
  (Client.still_valid): Bug fix: use "getattr(self, started, False)"
                        instead of "self.started" in case this client
                        object is so new that the "started" attribute
                        has not been created yet.
  (Client.IntervalChanged, Client.CheckerIsRunning, Client.GetChecker,
  Client.GetCreated, Client.GetFingerprint, Client.GetHost,
  Client.GetInterval, Client.GetName, Client.GetStarted,
  Client.GetTimeout, Client.StateChanged, Client.TimeoutChanged):
  Removed; all callers changed.
  (Client.CheckerCompleted): Add "condition" and "command" arguments.
                             All callers changed.
  (Client.GetAllProperties, Client.PropertyChanged): New.
  (Client.StillValid): Renamed to "IsStillValid".
  (Client.StartChecker): Changed to its own function to avoid the
                         return value from "Client.start_checker()".
  (Client.Stop): Changed to its own function to avoid the return value
                 from "Client.stop()".
  (main): Try "_mandos" before "mandos" as user and group name.
          Removed inner function "remove_from_clients".  New inner
          class "MandosServer".

Show diffs side-by-side

added added

removed removed

Lines of Context:
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 Teddy Hogeborn
 
13
 * Copyright © 2008 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
32
33
#define _LARGEFILE_SOURCE
33
34
#define _FILE_OFFSET_BITS 64
34
35
 
35
 
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY() */
 
36
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY(), asprintf() */
36
37
 
37
 
#include <stdio.h>
38
 
#include <assert.h>
39
 
#include <stdlib.h>
40
 
#include <time.h>
41
 
#include <net/if.h>             /* if_nametoindex */
42
 
#include <sys/ioctl.h>          /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
43
 
                                   SIOCSIFFLAGS */
 
38
#include <stdio.h>              /* fprintf(), stderr, fwrite(),
 
39
                                   stdout, ferror() */
 
40
#include <stdint.h>             /* uint16_t, uint32_t */
 
41
#include <stddef.h>             /* NULL, size_t, ssize_t */
 
42
#include <stdlib.h>             /* free(), EXIT_SUCCESS, EXIT_FAILURE,
 
43
                                   srand() */
 
44
#include <stdbool.h>            /* bool, true */
 
45
#include <string.h>             /* memset(), strcmp(), strlen(),
 
46
                                   strerror(), asprintf(), strcpy() */
 
47
#include <sys/ioctl.h>          /* ioctl */
 
48
#include <sys/types.h>          /* socket(), inet_pton(), sockaddr,
 
49
                                   sockaddr_in6, PF_INET6,
 
50
                                   SOCK_STREAM, INET6_ADDRSTRLEN,
 
51
                                   uid_t, gid_t, open(), opendir(), DIR */
 
52
#include <sys/stat.h>           /* open() */
 
53
#include <sys/socket.h>         /* socket(), struct sockaddr_in6,
 
54
                                   struct in6_addr, inet_pton(),
 
55
                                   connect() */
 
56
#include <fcntl.h>              /* open() */
 
57
#include <dirent.h>             /* opendir(), struct dirent, readdir() */
 
58
#include <inttypes.h>           /* PRIu16 */
 
59
#include <assert.h>             /* assert() */
 
60
#include <errno.h>              /* perror(), errno */
 
61
#include <time.h>               /* time() */
44
62
#include <net/if.h>             /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
45
 
                                   SIOCSIFFLAGS */
 
63
                                   SIOCSIFFLAGS, if_indextoname(),
 
64
                                   if_nametoindex(), IF_NAMESIZE */
 
65
#include <netinet/in.h>
 
66
#include <unistd.h>             /* close(), SEEK_SET, off_t, write(),
 
67
                                   getuid(), getgid(), setuid(),
 
68
                                   setgid() */
 
69
#include <arpa/inet.h>          /* inet_pton(), htons */
 
70
#include <iso646.h>             /* not, and */
 
71
#include <argp.h>               /* struct argp_option, error_t, struct
 
72
                                   argp_state, struct argp,
 
73
                                   argp_parse(), ARGP_KEY_ARG,
 
74
                                   ARGP_KEY_END, ARGP_ERR_UNKNOWN */
46
75
 
 
76
/* Avahi */
 
77
/* All Avahi types, constants and functions
 
78
 Avahi*, avahi_*,
 
79
 AVAHI_* */
47
80
#include <avahi-core/core.h>
48
81
#include <avahi-core/lookup.h>
49
82
#include <avahi-core/log.h>
51
84
#include <avahi-common/malloc.h>
52
85
#include <avahi-common/error.h>
53
86
 
54
 
/* Mandos client part */
55
 
#include <sys/types.h>          /* socket(), inet_pton() */
56
 
#include <sys/socket.h>         /* socket(), struct sockaddr_in6,
57
 
                                   struct in6_addr, inet_pton() */
58
 
#include <gnutls/gnutls.h>      /* All GnuTLS stuff */
59
 
#include <gnutls/openpgp.h>     /* GnuTLS with openpgp stuff */
60
 
 
61
 
#include <unistd.h>             /* close() */
62
 
#include <netinet/in.h>
63
 
#include <stdbool.h>            /* true */
64
 
#include <string.h>             /* memset */
65
 
#include <arpa/inet.h>          /* inet_pton() */
66
 
#include <iso646.h>             /* not */
67
 
#include <net/if.h>             /* IF_NAMESIZE */
 
87
/* GnuTLS */
 
88
#include <gnutls/gnutls.h>      /* All GnuTLS types, constants and
 
89
                                   functions:
 
90
                                   gnutls_*
 
91
                                   init_gnutls_session(),
 
92
                                   GNUTLS_* */
 
93
#include <gnutls/openpgp.h>     /* gnutls_certificate_set_openpgp_key_file(),
 
94
                                   GNUTLS_OPENPGP_FMT_BASE64 */
68
95
 
69
96
/* GPGME */
70
 
#include <errno.h>              /* perror() */
71
 
#include <gpgme.h>
72
 
 
73
 
/* getopt_long */
74
 
#include <getopt.h>
 
97
#include <gpgme.h>              /* All GPGME types, constants and
 
98
                                   functions:
 
99
                                   gpgme_*
 
100
                                   GPGME_PROTOCOL_OpenPGP,
 
101
                                   GPG_ERR_NO_* */
75
102
 
76
103
#define BUFFER_SIZE 256
77
104
 
78
 
static const char *keydir = "/conf/conf.d/mandos";
79
 
static const char *pubkeyfile = "pubkey.txt";
80
 
static const char *seckeyfile = "seckey.txt";
 
105
#define PATHDIR "/conf/conf.d/mandos"
 
106
#define SECKEY "seckey.txt"
 
107
#define PUBKEY "pubkey.txt"
81
108
 
82
109
bool debug = false;
83
 
 
84
 
const char mandos_protocol_version[] = "1";
85
 
 
86
 
/* Used for passing in values through all the callback functions */
 
110
static const char mandos_protocol_version[] = "1";
 
111
const char *argp_program_version = "mandos-client " VERSION;
 
112
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
 
113
 
 
114
/* Used for passing in values through the Avahi callback functions */
87
115
typedef struct {
88
116
  AvahiSimplePoll *simple_poll;
89
117
  AvahiServer *server;
90
118
  gnutls_certificate_credentials_t cred;
91
119
  unsigned int dh_bits;
 
120
  gnutls_dh_params_t dh_params;
92
121
  const char *priority;
 
122
  gpgme_ctx_t ctx;
93
123
} mandos_context;
94
124
 
 
125
/*
 
126
 * Make room in "buffer" for at least BUFFER_SIZE additional bytes.
 
127
 * "buffer_capacity" is how much is currently allocated,
 
128
 * "buffer_length" is how much is already used.
 
129
 */
95
130
size_t adjustbuffer(char **buffer, size_t buffer_length,
96
131
                  size_t buffer_capacity){
97
132
  if (buffer_length + BUFFER_SIZE > buffer_capacity){
105
140
}
106
141
 
107
142
/* 
108
 
 * Decrypt OpenPGP data using keyrings in HOMEDIR.
109
 
 * Returns -1 on error
 
143
 * Initialize GPGME.
110
144
 */
111
 
static ssize_t pgp_packet_decrypt (const char *cryptotext,
112
 
                                   size_t crypto_size,
113
 
                                   char **plaintext,
114
 
                                   const char *homedir){
115
 
  gpgme_data_t dh_crypto, dh_plain;
116
 
  gpgme_ctx_t ctx;
 
145
static bool init_gpgme(mandos_context *mc, const char *seckey,
 
146
                       const char *pubkey, const char *tempdir){
 
147
  int ret;
117
148
  gpgme_error_t rc;
118
 
  ssize_t ret;
119
 
  size_t plaintext_capacity = 0;
120
 
  ssize_t plaintext_length = 0;
121
149
  gpgme_engine_info_t engine_info;
122
150
  
 
151
  
 
152
  /*
 
153
   * Helper function to insert pub and seckey to the enigne keyring.
 
154
   */
 
155
  bool import_key(const char *filename){
 
156
    int fd;
 
157
    gpgme_data_t pgp_data;
 
158
    
 
159
    fd = TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
 
160
    if(fd == -1){
 
161
      perror("open");
 
162
      return false;
 
163
    }
 
164
    
 
165
    rc = gpgme_data_new_from_fd(&pgp_data, fd);
 
166
    if (rc != GPG_ERR_NO_ERROR){
 
167
      fprintf(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
 
168
              gpgme_strsource(rc), gpgme_strerror(rc));
 
169
      return false;
 
170
    }
 
171
    
 
172
    rc = gpgme_op_import(mc->ctx, pgp_data);
 
173
    if (rc != GPG_ERR_NO_ERROR){
 
174
      fprintf(stderr, "bad gpgme_op_import: %s: %s\n",
 
175
              gpgme_strsource(rc), gpgme_strerror(rc));
 
176
      return false;
 
177
    }
 
178
    
 
179
    ret = TEMP_FAILURE_RETRY(close(fd));
 
180
    if(ret == -1){
 
181
      perror("close");
 
182
    }
 
183
    gpgme_data_release(pgp_data);
 
184
    return true;
 
185
  }
 
186
  
123
187
  if (debug){
124
 
    fprintf(stderr, "Trying to decrypt OpenPGP data\n");
 
188
    fprintf(stderr, "Initialize gpgme\n");
125
189
  }
126
190
  
127
191
  /* Init GPGME */
130
194
  if (rc != GPG_ERR_NO_ERROR){
131
195
    fprintf(stderr, "bad gpgme_engine_check_version: %s: %s\n",
132
196
            gpgme_strsource(rc), gpgme_strerror(rc));
133
 
    return -1;
 
197
    return false;
134
198
  }
135
199
  
136
 
  /* Set GPGME home directory for the OpenPGP engine only */
 
200
    /* Set GPGME home directory for the OpenPGP engine only */
137
201
  rc = gpgme_get_engine_info (&engine_info);
138
202
  if (rc != GPG_ERR_NO_ERROR){
139
203
    fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
140
204
            gpgme_strsource(rc), gpgme_strerror(rc));
141
 
    return -1;
 
205
    return false;
142
206
  }
143
207
  while(engine_info != NULL){
144
208
    if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
145
209
      gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
146
 
                            engine_info->file_name, homedir);
 
210
                            engine_info->file_name, tempdir);
147
211
      break;
148
212
    }
149
213
    engine_info = engine_info->next;
150
214
  }
151
215
  if(engine_info == NULL){
152
 
    fprintf(stderr, "Could not set GPGME home dir to %s\n", homedir);
153
 
    return -1;
 
216
    fprintf(stderr, "Could not set GPGME home dir to %s\n", tempdir);
 
217
    return false;
 
218
  }
 
219
  
 
220
  /* Create new GPGME "context" */
 
221
  rc = gpgme_new(&(mc->ctx));
 
222
  if (rc != GPG_ERR_NO_ERROR){
 
223
    fprintf(stderr, "bad gpgme_new: %s: %s\n",
 
224
            gpgme_strsource(rc), gpgme_strerror(rc));
 
225
    return false;
 
226
  }
 
227
  
 
228
  if (not import_key(pubkey) or not import_key(seckey)){
 
229
    return false;
 
230
  }
 
231
  
 
232
  return true; 
 
233
}
 
234
 
 
235
/* 
 
236
 * Decrypt OpenPGP data.
 
237
 * Returns -1 on error
 
238
 */
 
239
static ssize_t pgp_packet_decrypt (const mandos_context *mc,
 
240
                                   const char *cryptotext,
 
241
                                   size_t crypto_size,
 
242
                                   char **plaintext){
 
243
  gpgme_data_t dh_crypto, dh_plain;
 
244
  gpgme_error_t rc;
 
245
  ssize_t ret;
 
246
  size_t plaintext_capacity = 0;
 
247
  ssize_t plaintext_length = 0;
 
248
  
 
249
  if (debug){
 
250
    fprintf(stderr, "Trying to decrypt OpenPGP data\n");
154
251
  }
155
252
  
156
253
  /* Create new GPGME data buffer from memory cryptotext */
171
268
    return -1;
172
269
  }
173
270
  
174
 
  /* Create new GPGME "context" */
175
 
  rc = gpgme_new(&ctx);
176
 
  if (rc != GPG_ERR_NO_ERROR){
177
 
    fprintf(stderr, "bad gpgme_new: %s: %s\n",
178
 
            gpgme_strsource(rc), gpgme_strerror(rc));
179
 
    plaintext_length = -1;
180
 
    goto decrypt_end;
181
 
  }
182
 
  
183
271
  /* Decrypt data from the cryptotext data buffer to the plaintext
184
272
     data buffer */
185
 
  rc = gpgme_op_decrypt(ctx, dh_crypto, dh_plain);
 
273
  rc = gpgme_op_decrypt(mc->ctx, dh_crypto, dh_plain);
186
274
  if (rc != GPG_ERR_NO_ERROR){
187
275
    fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
188
276
            gpgme_strsource(rc), gpgme_strerror(rc));
189
277
    plaintext_length = -1;
 
278
    if (debug){
 
279
      gpgme_decrypt_result_t result;
 
280
      result = gpgme_op_decrypt_result(mc->ctx);
 
281
      if (result == NULL){
 
282
        fprintf(stderr, "gpgme_op_decrypt_result failed\n");
 
283
      } else {
 
284
        fprintf(stderr, "Unsupported algorithm: %s\n",
 
285
                result->unsupported_algorithm);
 
286
        fprintf(stderr, "Wrong key usage: %u\n",
 
287
                result->wrong_key_usage);
 
288
        if(result->file_name != NULL){
 
289
          fprintf(stderr, "File name: %s\n", result->file_name);
 
290
        }
 
291
        gpgme_recipient_t recipient;
 
292
        recipient = result->recipients;
 
293
        if(recipient){
 
294
          while(recipient != NULL){
 
295
            fprintf(stderr, "Public key algorithm: %s\n",
 
296
                    gpgme_pubkey_algo_name(recipient->pubkey_algo));
 
297
            fprintf(stderr, "Key ID: %s\n", recipient->keyid);
 
298
            fprintf(stderr, "Secret key available: %s\n",
 
299
                    recipient->status == GPG_ERR_NO_SECKEY
 
300
                    ? "No" : "Yes");
 
301
            recipient = recipient->next;
 
302
          }
 
303
        }
 
304
      }
 
305
    }
190
306
    goto decrypt_end;
191
307
  }
192
308
  
194
310
    fprintf(stderr, "Decryption of OpenPGP data succeeded\n");
195
311
  }
196
312
  
197
 
  if (debug){
198
 
    gpgme_decrypt_result_t result;
199
 
    result = gpgme_op_decrypt_result(ctx);
200
 
    if (result == NULL){
201
 
      fprintf(stderr, "gpgme_op_decrypt_result failed\n");
202
 
    } else {
203
 
      fprintf(stderr, "Unsupported algorithm: %s\n",
204
 
              result->unsupported_algorithm);
205
 
      fprintf(stderr, "Wrong key usage: %d\n",
206
 
              result->wrong_key_usage);
207
 
      if(result->file_name != NULL){
208
 
        fprintf(stderr, "File name: %s\n", result->file_name);
209
 
      }
210
 
      gpgme_recipient_t recipient;
211
 
      recipient = result->recipients;
212
 
      if(recipient){
213
 
        while(recipient != NULL){
214
 
          fprintf(stderr, "Public key algorithm: %s\n",
215
 
                  gpgme_pubkey_algo_name(recipient->pubkey_algo));
216
 
          fprintf(stderr, "Key ID: %s\n", recipient->keyid);
217
 
          fprintf(stderr, "Secret key available: %s\n",
218
 
                  recipient->status == GPG_ERR_NO_SECKEY
219
 
                  ? "No" : "Yes");
220
 
          recipient = recipient->next;
221
 
        }
222
 
      }
223
 
    }
224
 
  }
225
 
  
226
313
  /* Seek back to the beginning of the GPGME plaintext data buffer */
227
314
  if (gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET) == -1){
228
 
    perror("pgpme_data_seek");
 
315
    perror("gpgme_data_seek");
229
316
    plaintext_length = -1;
230
317
    goto decrypt_end;
231
318
  }
232
319
  
233
320
  *plaintext = NULL;
234
321
  while(true){
235
 
    plaintext_capacity = adjustbuffer(plaintext, (size_t)plaintext_length,
 
322
    plaintext_capacity = adjustbuffer(plaintext,
 
323
                                      (size_t)plaintext_length,
236
324
                                      plaintext_capacity);
237
325
    if (plaintext_capacity == 0){
238
326
        perror("adjustbuffer");
254
342
    }
255
343
    plaintext_length += ret;
256
344
  }
257
 
 
 
345
  
258
346
  if(debug){
259
347
    fprintf(stderr, "Decrypted password is: ");
260
348
    for(ssize_t i = 0; i < plaintext_length; i++){
274
362
}
275
363
 
276
364
static const char * safer_gnutls_strerror (int value) {
277
 
  const char *ret = gnutls_strerror (value);
 
365
  const char *ret = gnutls_strerror (value); /* Spurious warning */
278
366
  if (ret == NULL)
279
367
    ret = "(unknown)";
280
368
  return ret;
286
374
  fprintf(stderr, "GnuTLS: %s", string);
287
375
}
288
376
 
289
 
static int initgnutls(mandos_context *mc, gnutls_session_t *session,
290
 
                      gnutls_dh_params_t *dh_params){
 
377
static int init_gnutls_global(mandos_context *mc,
 
378
                              const char *pubkeyfilename,
 
379
                              const char *seckeyfilename){
291
380
  int ret;
292
381
  
293
382
  if(debug){
294
383
    fprintf(stderr, "Initializing GnuTLS\n");
295
384
  }
296
 
 
297
 
  if ((ret = gnutls_global_init ())
298
 
      != GNUTLS_E_SUCCESS) {
 
385
  
 
386
  ret = gnutls_global_init();
 
387
  if (ret != GNUTLS_E_SUCCESS) {
299
388
    fprintf (stderr, "GnuTLS global_init: %s\n",
300
389
             safer_gnutls_strerror(ret));
301
390
    return -1;
310
399
  }
311
400
  
312
401
  /* OpenPGP credentials */
313
 
  if ((ret = gnutls_certificate_allocate_credentials (&mc->cred))
314
 
      != GNUTLS_E_SUCCESS) {
315
 
    fprintf (stderr, "GnuTLS memory error: %s\n",
 
402
  gnutls_certificate_allocate_credentials(&mc->cred);
 
403
  if (ret != GNUTLS_E_SUCCESS){
 
404
    fprintf (stderr, "GnuTLS memory error: %s\n", /* Spurious
 
405
                                                     warning */
316
406
             safer_gnutls_strerror(ret));
 
407
    gnutls_global_deinit ();
317
408
    return -1;
318
409
  }
319
410
  
320
411
  if(debug){
321
 
    fprintf(stderr, "Attempting to use OpenPGP certificate %s"
322
 
            " and keyfile %s as GnuTLS credentials\n", pubkeyfile,
323
 
            seckeyfile);
 
412
    fprintf(stderr, "Attempting to use OpenPGP public key %s and"
 
413
            " secret key %s as GnuTLS credentials\n", pubkeyfilename,
 
414
            seckeyfilename);
324
415
  }
325
416
  
326
417
  ret = gnutls_certificate_set_openpgp_key_file
327
 
    (mc->cred, pubkeyfile, seckeyfile, GNUTLS_OPENPGP_FMT_BASE64);
 
418
    (mc->cred, pubkeyfilename, seckeyfilename,
 
419
     GNUTLS_OPENPGP_FMT_BASE64);
328
420
  if (ret != GNUTLS_E_SUCCESS) {
329
421
    fprintf(stderr,
330
422
            "Error[%d] while reading the OpenPGP key pair ('%s',"
331
 
            " '%s')\n", ret, pubkeyfile, seckeyfile);
332
 
    fprintf(stdout, "The GnuTLS error is: %s\n",
 
423
            " '%s')\n", ret, pubkeyfilename, seckeyfilename);
 
424
    fprintf(stderr, "The GnuTLS error is: %s\n",
333
425
            safer_gnutls_strerror(ret));
334
 
    return -1;
 
426
    goto globalfail;
335
427
  }
336
428
  
337
429
  /* GnuTLS server initialization */
338
 
  ret = gnutls_dh_params_init(dh_params);
 
430
  ret = gnutls_dh_params_init(&mc->dh_params);
339
431
  if (ret != GNUTLS_E_SUCCESS) {
340
432
    fprintf (stderr, "Error in GnuTLS DH parameter initialization:"
341
433
             " %s\n", safer_gnutls_strerror(ret));
342
 
    return -1;
 
434
    goto globalfail;
343
435
  }
344
 
  ret = gnutls_dh_params_generate2(*dh_params, mc->dh_bits);
 
436
  ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
345
437
  if (ret != GNUTLS_E_SUCCESS) {
346
438
    fprintf (stderr, "Error in GnuTLS prime generation: %s\n",
347
439
             safer_gnutls_strerror(ret));
348
 
    return -1;
 
440
    goto globalfail;
349
441
  }
350
442
  
351
 
  gnutls_certificate_set_dh_params(mc->cred, *dh_params);
352
 
  
 
443
  gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
 
444
  
 
445
  return 0;
 
446
  
 
447
 globalfail:
 
448
  
 
449
  gnutls_certificate_free_credentials(mc->cred);
 
450
  gnutls_global_deinit();
 
451
  gnutls_dh_params_deinit(mc->dh_params);
 
452
  return -1;
 
453
}
 
454
 
 
455
static int init_gnutls_session(mandos_context *mc,
 
456
                               gnutls_session_t *session){
 
457
  int ret;
353
458
  /* GnuTLS session creation */
354
459
  ret = gnutls_init(session, GNUTLS_SERVER);
355
460
  if (ret != GNUTLS_E_SUCCESS){
364
469
      fprintf(stderr, "Syntax error at: %s\n", err);
365
470
      fprintf(stderr, "GnuTLS error: %s\n",
366
471
              safer_gnutls_strerror(ret));
 
472
      gnutls_deinit (*session);
367
473
      return -1;
368
474
    }
369
475
  }
373
479
  if (ret != GNUTLS_E_SUCCESS) {
374
480
    fprintf(stderr, "Error setting GnuTLS credentials: %s\n",
375
481
            safer_gnutls_strerror(ret));
 
482
    gnutls_deinit (*session);
376
483
    return -1;
377
484
  }
378
485
  
394
501
                                      AvahiIfIndex if_index,
395
502
                                      mandos_context *mc){
396
503
  int ret, tcp_sd;
397
 
  struct sockaddr_in6 to;
 
504
  union { struct sockaddr in; struct sockaddr_in6 in6; } to;
398
505
  char *buffer = NULL;
399
506
  char *decrypted_buffer;
400
507
  size_t buffer_length = 0;
404
511
  int retval = 0;
405
512
  char interface[IF_NAMESIZE];
406
513
  gnutls_session_t session;
407
 
  gnutls_dh_params_t dh_params;
408
514
  
409
 
  ret = initgnutls (mc, &session, &dh_params);
 
515
  ret = init_gnutls_session (mc, &session);
410
516
  if (ret != 0){
411
517
    return -1;
412
518
  }
413
519
  
414
520
  if(debug){
415
 
    fprintf(stderr, "Setting up a tcp connection to %s, port %d\n",
416
 
            ip, port);
 
521
    fprintf(stderr, "Setting up a tcp connection to %s, port %" PRIu16
 
522
            "\n", ip, port);
417
523
  }
418
524
  
419
525
  tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
421
527
    perror("socket");
422
528
    return -1;
423
529
  }
424
 
 
 
530
  
425
531
  if(debug){
426
532
    if(if_indextoname((unsigned int)if_index, interface) == NULL){
427
533
      perror("if_indextoname");
430
536
    fprintf(stderr, "Binding to interface %s\n", interface);
431
537
  }
432
538
  
433
 
  memset(&to,0,sizeof(to));     /* Spurious warning */
434
 
  to.sin6_family = AF_INET6;
 
539
  memset(&to, 0, sizeof(to));
 
540
  to.in6.sin6_family = AF_INET6;
435
541
  /* It would be nice to have a way to detect if we were passed an
436
542
     IPv4 address here.   Now we assume an IPv6 address. */
437
 
  ret = inet_pton(AF_INET6, ip, &to.sin6_addr);
 
543
  ret = inet_pton(AF_INET6, ip, &to.in6.sin6_addr);
438
544
  if (ret < 0 ){
439
545
    perror("inet_pton");
440
546
    return -1;
443
549
    fprintf(stderr, "Bad address: %s\n", ip);
444
550
    return -1;
445
551
  }
446
 
  to.sin6_port = htons(port);   /* Spurious warning */
 
552
  to.in6.sin6_port = htons(port); /* Spurious warning */
447
553
  
448
 
  to.sin6_scope_id = (uint32_t)if_index;
 
554
  to.in6.sin6_scope_id = (uint32_t)if_index;
449
555
  
450
556
  if(debug){
451
 
    fprintf(stderr, "Connection to: %s, port %d\n", ip, port);
 
557
    fprintf(stderr, "Connection to: %s, port %" PRIu16 "\n", ip,
 
558
            port);
452
559
    char addrstr[INET6_ADDRSTRLEN] = "";
453
 
    if(inet_ntop(to.sin6_family, &(to.sin6_addr), addrstr,
 
560
    if(inet_ntop(to.in6.sin6_family, &(to.in6.sin6_addr), addrstr,
454
561
                 sizeof(addrstr)) == NULL){
455
562
      perror("inet_ntop");
456
563
    } else {
460
567
    }
461
568
  }
462
569
  
463
 
  ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
 
570
  ret = connect(tcp_sd, &to.in, sizeof(to));
464
571
  if (ret < 0){
465
572
    perror("connect");
466
573
    return -1;
467
574
  }
468
 
 
 
575
  
469
576
  const char *out = mandos_protocol_version;
470
577
  written = 0;
471
578
  while (true){
489
596
      }
490
597
    }
491
598
  }
492
 
 
 
599
  
493
600
  if(debug){
494
601
    fprintf(stderr, "Establishing TLS session with %s\n", ip);
495
602
  }
496
603
  
497
604
  gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t) tcp_sd);
498
605
  
499
 
  ret = gnutls_handshake (session);
 
606
  do{
 
607
    ret = gnutls_handshake (session);
 
608
  } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
500
609
  
501
610
  if (ret != GNUTLS_E_SUCCESS){
502
611
    if(debug){
513
622
    fprintf(stderr, "Retrieving pgp encrypted password from %s\n",
514
623
            ip);
515
624
  }
516
 
 
 
625
  
517
626
  while(true){
518
 
    buffer_capacity = adjustbuffer(&buffer, buffer_length, buffer_capacity);
 
627
    buffer_capacity = adjustbuffer(&buffer, buffer_length,
 
628
                                   buffer_capacity);
519
629
    if (buffer_capacity == 0){
520
630
      perror("adjustbuffer");
521
631
      retval = -1;
533
643
      case GNUTLS_E_AGAIN:
534
644
        break;
535
645
      case GNUTLS_E_REHANDSHAKE:
536
 
        ret = gnutls_handshake (session);
 
646
        do{
 
647
          ret = gnutls_handshake (session);
 
648
        } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
537
649
        if (ret < 0){
538
650
          fprintf(stderr, "*** GnuTLS Re-handshake failed ***\n");
539
651
          gnutls_perror (ret);
560
672
  gnutls_bye (session, GNUTLS_SHUT_RDWR);
561
673
  
562
674
  if (buffer_length > 0){
563
 
    decrypted_buffer_size = pgp_packet_decrypt(buffer,
 
675
    decrypted_buffer_size = pgp_packet_decrypt(mc, buffer,
564
676
                                               buffer_length,
565
 
                                               &decrypted_buffer,
566
 
                                               keydir);
 
677
                                               &decrypted_buffer);
567
678
    if (decrypted_buffer_size >= 0){
568
679
      written = 0;
569
680
      while(written < (size_t) decrypted_buffer_size){
584
695
    } else {
585
696
      retval = -1;
586
697
    }
 
698
  } else {
 
699
    retval = -1;
587
700
  }
588
701
  
589
702
  /* Shutdown procedure */
590
703
  
591
704
 mandos_end:
592
705
  free(buffer);
593
 
  close(tcp_sd);
 
706
  ret = TEMP_FAILURE_RETRY(close(tcp_sd));
 
707
  if(ret == -1){
 
708
    perror("close");
 
709
  }
594
710
  gnutls_deinit (session);
595
 
  gnutls_certificate_free_credentials (mc->cred);
596
 
  gnutls_global_deinit ();
597
711
  return retval;
598
712
}
599
713
 
612
726
                             flags,
613
727
                             void* userdata) {
614
728
  mandos_context *mc = userdata;
615
 
  assert(r);                    /* Spurious warning */
 
729
  assert(r);
616
730
  
617
731
  /* Called whenever a service has been resolved successfully or
618
732
     timed out */
630
744
      char ip[AVAHI_ADDRESS_STR_MAX];
631
745
      avahi_address_snprint(ip, sizeof(ip), address);
632
746
      if(debug){
633
 
        fprintf(stderr, "Mandos server \"%s\" found on %s (%s, %d) on"
634
 
                " port %d\n", name, host_name, ip, interface, port);
 
747
        fprintf(stderr, "Mandos server \"%s\" found on %s (%s, %"
 
748
                PRIu16 ") on port %d\n", name, host_name, ip,
 
749
                interface, port);
635
750
      }
636
751
      int ret = start_mandos_communication(ip, port, interface, mc);
637
752
      if (ret == 0){
638
 
        exit(EXIT_SUCCESS);
 
753
        avahi_simple_poll_quit(mc->simple_poll);
639
754
      }
640
755
    }
641
756
  }
653
768
                             flags,
654
769
                             void* userdata) {
655
770
  mandos_context *mc = userdata;
656
 
  assert(b);                    /* Spurious warning */
 
771
  assert(b);
657
772
  
658
773
  /* Called whenever a new services becomes available on the LAN or
659
774
     is removed from the LAN */
693
808
  }
694
809
}
695
810
 
696
 
/* Combines file name and path and returns the malloced new
697
 
   string. some sane checks could/should be added */
698
 
static const char *combinepath(const char *first, const char *second){
699
 
  size_t f_len = strlen(first);
700
 
  size_t s_len = strlen(second);
701
 
  char *tmp = malloc(f_len + s_len + 2);
702
 
  if (tmp == NULL){
703
 
    return NULL;
704
 
  }
705
 
  if(f_len > 0){
706
 
    memcpy(tmp, first, f_len);  /* Spurious warning */
707
 
  }
708
 
  tmp[f_len] = '/';
709
 
  if(s_len > 0){
710
 
    memcpy(tmp + f_len + 1, second, s_len); /* Spurious warning */
711
 
  }
712
 
  tmp[f_len + 1 + s_len] = '\0';
713
 
  return tmp;
714
 
}
715
 
 
716
 
 
717
811
int main(int argc, char *argv[]){
718
812
    AvahiSServiceBrowser *sb = NULL;
719
813
    int error;
722
816
    const char *interface = "eth0";
723
817
    struct ifreq network;
724
818
    int sd;
 
819
    uid_t uid;
 
820
    gid_t gid;
725
821
    char *connect_to = NULL;
 
822
    char tempdir[] = "/tmp/mandosXXXXXX";
726
823
    AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
 
824
    const char *seckey = PATHDIR "/" SECKEY;
 
825
    const char *pubkey = PATHDIR "/" PUBKEY;
 
826
    
727
827
    mandos_context mc = { .simple_poll = NULL, .server = NULL,
728
 
                          .dh_bits = 1024, .priority = "SECURE256"};
 
828
                          .dh_bits = 1024, .priority = "SECURE256"
 
829
                          ":!CTYPE-X.509:+CTYPE-OPENPGP" };
 
830
    bool gnutls_initalized = false;
 
831
    bool gpgme_initalized = false;
729
832
    
730
833
    {
731
 
      /* Temporary int to get the address of for getopt_long */
732
 
      int debug_int = debug ? 1 : 0;
733
 
      while (true){
734
 
        struct option long_options[] = {
735
 
          {"debug", no_argument, &debug_int, 1},
736
 
          {"connect", required_argument, NULL, 'c'},
737
 
          {"interface", required_argument, NULL, 'i'},
738
 
          {"keydir", required_argument, NULL, 'd'},
739
 
          {"seckey", required_argument, NULL, 's'},
740
 
          {"pubkey", required_argument, NULL, 'p'},
741
 
          {"dh-bits", required_argument, NULL, 'D'},
742
 
          {"priority", required_argument, NULL, 'P'},
743
 
          {0, 0, 0, 0} };
744
 
      
745
 
        int option_index = 0;
746
 
        ret = getopt_long (argc, argv, "i:", long_options,
747
 
                           &option_index);
748
 
      
749
 
        if (ret == -1){
750
 
          break;
751
 
        }
752
 
      
753
 
        switch(ret){
754
 
        case 0:
755
 
          break;
756
 
        case 'i':
757
 
          interface = optarg;
758
 
          break;
759
 
        case 'c':
760
 
          connect_to = optarg;
761
 
          break;
762
 
        case 'd':
763
 
          keydir = optarg;
764
 
          break;
765
 
        case 'p':
766
 
          pubkeyfile = optarg;
767
 
          break;
768
 
        case 's':
769
 
          seckeyfile = optarg;
770
 
          break;
771
 
        case 'D':
 
834
      struct argp_option options[] = {
 
835
        { .name = "debug", .key = 128,
 
836
          .doc = "Debug mode", .group = 3 },
 
837
        { .name = "connect", .key = 'c',
 
838
          .arg = "ADDRESS:PORT",
 
839
          .doc = "Connect directly to a specific Mandos server",
 
840
          .group = 1 },
 
841
        { .name = "interface", .key = 'i',
 
842
          .arg = "NAME",
 
843
          .doc = "Interface that will be used to search for Mandos"
 
844
          " servers",
 
845
          .group = 1 },
 
846
        { .name = "seckey", .key = 's',
 
847
          .arg = "FILE",
 
848
          .doc = "OpenPGP secret key file base name",
 
849
          .group = 1 },
 
850
        { .name = "pubkey", .key = 'p',
 
851
          .arg = "FILE",
 
852
          .doc = "OpenPGP public key file base name",
 
853
          .group = 2 },
 
854
        { .name = "dh-bits", .key = 129,
 
855
          .arg = "BITS",
 
856
          .doc = "Bit length of the prime number used in the"
 
857
          " Diffie-Hellman key exchange",
 
858
          .group = 2 },
 
859
        { .name = "priority", .key = 130,
 
860
          .arg = "STRING",
 
861
          .doc = "GnuTLS priority string for the TLS handshake",
 
862
          .group = 1 },
 
863
        { .name = NULL }
 
864
      };
 
865
      
 
866
      error_t parse_opt (int key, char *arg,
 
867
                         struct argp_state *state) {
 
868
        /* Get the INPUT argument from `argp_parse', which we know is
 
869
           a pointer to our plugin list pointer. */
 
870
        switch (key) {
 
871
        case 128:               /* --debug */
 
872
          debug = true;
 
873
          break;
 
874
        case 'c':               /* --connect */
 
875
          connect_to = arg;
 
876
          break;
 
877
        case 'i':               /* --interface */
 
878
          interface = arg;
 
879
          break;
 
880
        case 's':               /* --seckey */
 
881
          seckey = arg;
 
882
          break;
 
883
        case 'p':               /* --pubkey */
 
884
          pubkey = arg;
 
885
          break;
 
886
        case 129:               /* --dh-bits */
772
887
          errno = 0;
773
 
          mc.dh_bits = (unsigned int) strtol(optarg, NULL, 10);
 
888
          mc.dh_bits = (unsigned int) strtol(arg, NULL, 10);
774
889
          if (errno){
775
890
            perror("strtol");
776
891
            exit(EXIT_FAILURE);
777
892
          }
778
893
          break;
779
 
        case 'P':
780
 
          mc.priority = optarg;
781
 
          break;
782
 
        case '?':
 
894
        case 130:               /* --priority */
 
895
          mc.priority = arg;
 
896
          break;
 
897
        case ARGP_KEY_ARG:
 
898
          argp_usage (state);
 
899
        case ARGP_KEY_END:
 
900
          break;
783
901
        default:
784
 
          /* getopt_long() has already printed a message about the
785
 
             unrcognized option, so just exit. */
786
 
          exit(EXIT_FAILURE);
787
 
        }
788
 
      }
789
 
      /* Set the global debug flag from the temporary int */
790
 
      debug = debug_int ? true : false;
791
 
    }
792
 
    
793
 
    pubkeyfile = combinepath(keydir, pubkeyfile);
794
 
    if (pubkeyfile == NULL){
795
 
      perror("combinepath");
796
 
      exitcode = EXIT_FAILURE;
797
 
      goto end;
798
 
    }
799
 
    
800
 
    seckeyfile = combinepath(keydir, seckeyfile);
801
 
    if (seckeyfile == NULL){
802
 
      perror("combinepath");
803
 
      goto end;
 
902
          return ARGP_ERR_UNKNOWN;
 
903
        }
 
904
        return 0;
 
905
      }
 
906
      
 
907
      struct argp argp = { .options = options, .parser = parse_opt,
 
908
                           .args_doc = "",
 
909
                           .doc = "Mandos client -- Get and decrypt"
 
910
                           " passwords from a Mandos server" };
 
911
      ret = argp_parse (&argp, argc, argv, 0, 0, NULL);
 
912
      if (ret == ARGP_ERR_UNKNOWN){
 
913
        fprintf(stderr, "Unknown error while parsing arguments\n");
 
914
        exitcode = EXIT_FAILURE;
 
915
        goto end;
 
916
      }
 
917
    }
 
918
    
 
919
    /* If the interface is down, bring it up */
 
920
    {
 
921
      sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
 
922
      if(sd < 0) {
 
923
        perror("socket");
 
924
        exitcode = EXIT_FAILURE;
 
925
        goto end;
 
926
      }
 
927
      strcpy(network.ifr_name, interface);
 
928
      ret = ioctl(sd, SIOCGIFFLAGS, &network);
 
929
      if(ret == -1){
 
930
        perror("ioctl SIOCGIFFLAGS");
 
931
        exitcode = EXIT_FAILURE;
 
932
        goto end;
 
933
      }
 
934
      if((network.ifr_flags & IFF_UP) == 0){
 
935
        network.ifr_flags |= IFF_UP;
 
936
        ret = ioctl(sd, SIOCSIFFLAGS, &network);
 
937
        if(ret == -1){
 
938
          perror("ioctl SIOCSIFFLAGS");
 
939
          exitcode = EXIT_FAILURE;
 
940
          goto end;
 
941
        }
 
942
      }
 
943
      ret = TEMP_FAILURE_RETRY(close(sd));
 
944
      if(ret == -1){
 
945
        perror("close");
 
946
      }
 
947
    }
 
948
    
 
949
    uid = getuid();
 
950
    gid = getgid();
 
951
    
 
952
    ret = setuid(uid);
 
953
    if (ret == -1){
 
954
      perror("setuid");
 
955
    }
 
956
    
 
957
    setgid(gid);
 
958
    if (ret == -1){
 
959
      perror("setgid");
 
960
    }
 
961
    
 
962
    ret = init_gnutls_global(&mc, pubkey, seckey);
 
963
    if (ret == -1){
 
964
      fprintf(stderr, "init_gnutls_global failed\n");
 
965
      exitcode = EXIT_FAILURE;
 
966
      goto end;
 
967
    } else {
 
968
      gnutls_initalized = true;
 
969
    }
 
970
    
 
971
    if(mkdtemp(tempdir) == NULL){
 
972
      perror("mkdtemp");
 
973
      tempdir[0] = '\0';
 
974
      goto end;
 
975
    }
 
976
    
 
977
    if(not init_gpgme(&mc, pubkey, seckey, tempdir)){
 
978
      fprintf(stderr, "gpgme_initalized failed\n");
 
979
      exitcode = EXIT_FAILURE;
 
980
      goto end;
 
981
    } else {
 
982
      gpgme_initalized = true;
804
983
    }
805
984
    
806
985
    if_index = (AvahiIfIndex) if_nametoindex(interface);
815
994
      char *address = strrchr(connect_to, ':');
816
995
      if(address == NULL){
817
996
        fprintf(stderr, "No colon in address\n");
818
 
        exit(EXIT_FAILURE);
 
997
        exitcode = EXIT_FAILURE;
 
998
        goto end;
819
999
      }
820
1000
      errno = 0;
821
1001
      uint16_t port = (uint16_t) strtol(address+1, NULL, 10);
822
1002
      if(errno){
823
1003
        perror("Bad port number");
824
 
        exit(EXIT_FAILURE);
 
1004
        exitcode = EXIT_FAILURE;
 
1005
        goto end;
825
1006
      }
826
1007
      *address = '\0';
827
1008
      address = connect_to;
828
1009
      ret = start_mandos_communication(address, port, if_index, &mc);
829
1010
      if(ret < 0){
830
 
        exit(EXIT_FAILURE);
 
1011
        exitcode = EXIT_FAILURE;
831
1012
      } else {
832
 
        exit(EXIT_SUCCESS);
833
 
      }
834
 
    }
835
 
    
836
 
    /* If the interface is down, bring it up */
837
 
    {
838
 
      sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
839
 
      if(sd < 0) {
840
 
        perror("socket");
841
 
        exitcode = EXIT_FAILURE;
842
 
        goto end;
843
 
      }
844
 
      strcpy(network.ifr_name, interface); /* Spurious warning */
845
 
      ret = ioctl(sd, SIOCGIFFLAGS, &network);
846
 
      if(ret == -1){
847
 
        perror("ioctl SIOCGIFFLAGS");
848
 
        exitcode = EXIT_FAILURE;
849
 
        goto end;
850
 
      }
851
 
      if((network.ifr_flags & IFF_UP) == 0){
852
 
        network.ifr_flags |= IFF_UP;
853
 
        ret = ioctl(sd, SIOCSIFFLAGS, &network);
854
 
        if(ret == -1){
855
 
          perror("ioctl SIOCSIFFLAGS");
856
 
          exitcode = EXIT_FAILURE;
857
 
          goto end;
858
 
        }
859
 
      }
860
 
      close(sd);
 
1013
        exitcode = EXIT_SUCCESS;
 
1014
      }
 
1015
      goto end;
861
1016
    }
862
1017
    
863
1018
    if (not debug){
875
1030
        exitcode = EXIT_FAILURE;
876
1031
        goto end;
877
1032
    }
878
 
 
 
1033
    
879
1034
    {
880
1035
      AvahiServerConfig config;
881
1036
      /* Do not publish any local Zeroconf records */
884
1039
      config.publish_addresses = 0;
885
1040
      config.publish_workstation = 0;
886
1041
      config.publish_domain = 0;
887
 
 
 
1042
      
888
1043
      /* Allocate a new server */
889
1044
      mc.server = avahi_server_new(avahi_simple_poll_get
890
1045
                                   (mc.simple_poll), &config, NULL,
891
1046
                                   NULL, &error);
892
 
    
 
1047
      
893
1048
      /* Free the Avahi configuration data */
894
1049
      avahi_server_config_free(&config);
895
1050
    }
915
1070
    }
916
1071
    
917
1072
    /* Run the main loop */
918
 
 
 
1073
    
919
1074
    if (debug){
920
1075
      fprintf(stderr, "Starting Avahi loop search\n");
921
1076
    }
923
1078
    avahi_simple_poll_loop(mc.simple_poll);
924
1079
    
925
1080
 end:
926
 
 
 
1081
    
927
1082
    if (debug){
928
1083
      fprintf(stderr, "%s exiting\n", argv[0]);
929
1084
    }
934
1089
    
935
1090
    if (mc.server != NULL)
936
1091
        avahi_server_free(mc.server);
937
 
 
 
1092
    
938
1093
    if (mc.simple_poll != NULL)
939
1094
        avahi_simple_poll_free(mc.simple_poll);
940
 
    free(pubkeyfile);
941
 
    free(seckeyfile);
942
 
    
 
1095
    
 
1096
    if (gnutls_initalized){
 
1097
      gnutls_certificate_free_credentials(mc.cred);
 
1098
      gnutls_global_deinit ();
 
1099
      gnutls_dh_params_deinit(mc.dh_params);
 
1100
    }
 
1101
    
 
1102
    if(gpgme_initalized){
 
1103
      gpgme_release(mc.ctx);
 
1104
    }
 
1105
    
 
1106
    /* Removes the temp directory used by GPGME */
 
1107
    if(tempdir[0] != '\0'){
 
1108
      DIR *d;
 
1109
      struct dirent *direntry;
 
1110
      d = opendir(tempdir);
 
1111
      if(d == NULL){
 
1112
        perror("opendir");
 
1113
      } else {
 
1114
        while(true){
 
1115
          direntry = readdir(d);
 
1116
          if(direntry == NULL){
 
1117
            break;
 
1118
          }
 
1119
          if (direntry->d_type == DT_REG){
 
1120
            char *fullname = NULL;
 
1121
            ret = asprintf(&fullname, "%s/%s", tempdir,
 
1122
                           direntry->d_name);
 
1123
            if(ret < 0){
 
1124
              perror("asprintf");
 
1125
              continue;
 
1126
            }
 
1127
            ret = unlink(fullname);
 
1128
            if(ret == -1){
 
1129
              fprintf(stderr, "unlink(\"%s\"): %s",
 
1130
                      fullname, strerror(errno));
 
1131
            }
 
1132
            free(fullname);
 
1133
          }
 
1134
        }
 
1135
        closedir(d);
 
1136
      }
 
1137
      ret = rmdir(tempdir);
 
1138
      if(ret == -1){
 
1139
        perror("rmdir");
 
1140
      }
 
1141
    }
 
1142
          
943
1143
    return exitcode;
944
1144
}