/mandos/trunk

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

« back to all changes in this revision

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

  • Committer: Teddy Hogeborn
  • Date: 2009-01-15 05:27:55 UTC
  • mfrom: (237.2.9 mandos-release)
  • Revision ID: teddy@fukt.bsnet.se-20090115052755-luw0gjx8727p24o3
Merge from release branch.

Show diffs side-by-side

added added

removed removed

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