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

Added support for protocol version handling

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/*  -*- coding: utf-8 -*- */
2
2
/*
3
 
 * Mandos-client - get and decrypt data from a Mandos server
 
3
 * Mandos client - get and decrypt data from a Mandos server
4
4
 *
5
5
 * This program is partly derived from an example program for an Avahi
6
6
 * service browser, downloaded from
9
9
 * "browse_callback", and parts of "main".
10
10
 * 
11
11
 * Everything else is
12
 
 * Copyright © 2008,2009 Teddy Hogeborn
13
 
 * Copyright © 2008,2009 Björn Påhlsson
 
12
 * Copyright © 2007-2008 Teddy Hogeborn & Björn Påhlsson
14
13
 * 
15
14
 * This program is free software: you can redistribute it and/or
16
15
 * modify it under the terms of the GNU General Public License as
33
32
#define _LARGEFILE_SOURCE
34
33
#define _FILE_OFFSET_BITS 64
35
34
 
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() */
64
 
#include <net/if.h>             /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
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 */
77
 
 
78
 
/* Avahi */
79
 
/* All Avahi types, constants and functions
80
 
 Avahi*, avahi_*,
81
 
 AVAHI_* */
 
35
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY() */
 
36
 
 
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, SIOCSIFFLAGS
 
43
#include <net/if.h>             // ioctl, ifreq, SIOCGIFFLAGS, IFF_UP, SIOCSIFFLAGS
 
44
 
82
45
#include <avahi-core/core.h>
83
46
#include <avahi-core/lookup.h>
84
47
#include <avahi-core/log.h>
86
49
#include <avahi-common/malloc.h>
87
50
#include <avahi-common/error.h>
88
51
 
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_* */
 
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>
105
72
 
106
73
#define BUFFER_SIZE 256
 
74
#define DH_BITS 1024
107
75
 
108
 
#define PATHDIR "/conf/conf.d/mandos"
109
 
#define SECKEY "seckey.txt"
110
 
#define PUBKEY "pubkey.txt"
 
76
static const char *certdir = "/conf/conf.d/mandos";
 
77
static const char *certfile = "openpgp-client.txt";
 
78
static const char *certkey = "openpgp-client-key.txt";
111
79
 
112
80
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>";
116
 
 
117
 
/* Used for passing in values through the Avahi callback functions */
 
81
 
 
82
const char mandos_protocol_version[] = "1";
 
83
 
118
84
typedef struct {
119
85
  AvahiSimplePoll *simple_poll;
120
86
  AvahiServer *server;
121
87
  gnutls_certificate_credentials_t cred;
122
88
  unsigned int dh_bits;
123
 
  gnutls_dh_params_t dh_params;
124
89
  const char *priority;
125
 
  gpgme_ctx_t ctx;
126
90
} mandos_context;
127
91
 
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,
 
92
size_t adjustbuffer(char *buffer, size_t buffer_length,
134
93
                  size_t buffer_capacity){
135
 
  if(buffer_length + BUFFER_SIZE > buffer_capacity){
136
 
    *buffer = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
137
 
    if(buffer == NULL){
 
94
  if (buffer_length + BUFFER_SIZE > buffer_capacity){
 
95
    buffer = realloc(buffer, buffer_capacity + BUFFER_SIZE);
 
96
    if (buffer == NULL){
138
97
      return 0;
139
98
    }
140
99
    buffer_capacity += BUFFER_SIZE;
142
101
  return buffer_capacity;
143
102
}
144
103
 
145
 
/* 
146
 
 * Initialize GPGME.
147
 
 */
148
 
static bool init_gpgme(mandos_context *mc, const char *seckey,
149
 
                       const char *pubkey, const char *tempdir){
150
 
  int ret;
 
104
static ssize_t pgp_packet_decrypt (char *packet, size_t packet_size,
 
105
                                   char **new_packet,
 
106
                                   const char *homedir){
 
107
  gpgme_data_t dh_crypto, dh_plain;
 
108
  gpgme_ctx_t ctx;
151
109
  gpgme_error_t rc;
 
110
  ssize_t ret;
 
111
  size_t new_packet_capacity = 0;
 
112
  ssize_t new_packet_length = 0;
152
113
  gpgme_engine_info_t engine_info;
153
 
  
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");
 
114
 
 
115
  if (debug){
 
116
    fprintf(stderr, "Trying to decrypt OpenPGP packet\n");
192
117
  }
193
118
  
194
119
  /* Init GPGME */
195
120
  gpgme_check_version(NULL);
196
121
  rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
197
 
  if(rc != GPG_ERR_NO_ERROR){
 
122
  if (rc != GPG_ERR_NO_ERROR){
198
123
    fprintf(stderr, "bad gpgme_engine_check_version: %s: %s\n",
199
124
            gpgme_strsource(rc), gpgme_strerror(rc));
200
 
    return false;
 
125
    return -1;
201
126
  }
202
127
  
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){
 
128
  /* Set GPGME home directory */
 
129
  rc = gpgme_get_engine_info (&engine_info);
 
130
  if (rc != GPG_ERR_NO_ERROR){
206
131
    fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
207
132
            gpgme_strsource(rc), gpgme_strerror(rc));
208
 
    return false;
 
133
    return -1;
209
134
  }
210
135
  while(engine_info != NULL){
211
136
    if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
212
137
      gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
213
 
                            engine_info->file_name, tempdir);
 
138
                            engine_info->file_name, homedir);
214
139
      break;
215
140
    }
216
141
    engine_info = engine_info->next;
217
142
  }
218
143
  if(engine_info == NULL){
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");
254
 
  }
255
 
  
256
 
  /* Create new GPGME data buffer from memory cryptotext */
257
 
  rc = gpgme_data_new_from_mem(&dh_crypto, cryptotext, crypto_size,
258
 
                               0);
259
 
  if(rc != GPG_ERR_NO_ERROR){
 
144
    fprintf(stderr, "Could not set home dir to %s\n", homedir);
 
145
    return -1;
 
146
  }
 
147
  
 
148
  /* Create new GPGME data buffer from packet buffer */
 
149
  rc = gpgme_data_new_from_mem(&dh_crypto, packet, packet_size, 0);
 
150
  if (rc != GPG_ERR_NO_ERROR){
260
151
    fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
261
152
            gpgme_strsource(rc), gpgme_strerror(rc));
262
153
    return -1;
264
155
  
265
156
  /* Create new empty GPGME data buffer for the plaintext */
266
157
  rc = gpgme_data_new(&dh_plain);
267
 
  if(rc != GPG_ERR_NO_ERROR){
 
158
  if (rc != GPG_ERR_NO_ERROR){
268
159
    fprintf(stderr, "bad gpgme_data_new: %s: %s\n",
269
160
            gpgme_strsource(rc), gpgme_strerror(rc));
270
 
    gpgme_data_release(dh_crypto);
271
 
    return -1;
272
 
  }
273
 
  
274
 
  /* Decrypt data from the cryptotext data buffer to the plaintext
275
 
     data buffer */
276
 
  rc = gpgme_op_decrypt(mc->ctx, dh_crypto, dh_plain);
277
 
  if(rc != GPG_ERR_NO_ERROR){
 
161
    return -1;
 
162
  }
 
163
  
 
164
  /* Create new GPGME "context" */
 
165
  rc = gpgme_new(&ctx);
 
166
  if (rc != GPG_ERR_NO_ERROR){
 
167
    fprintf(stderr, "bad gpgme_new: %s: %s\n",
 
168
            gpgme_strsource(rc), gpgme_strerror(rc));
 
169
    return -1;
 
170
  }
 
171
  
 
172
  /* Decrypt data from the FILE pointer to the plaintext data
 
173
     buffer */
 
174
  rc = gpgme_op_decrypt(ctx, dh_crypto, dh_plain);
 
175
  if (rc != GPG_ERR_NO_ERROR){
278
176
    fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
279
177
            gpgme_strsource(rc), gpgme_strerror(rc));
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
 
          }
 
178
    return -1;
 
179
  }
 
180
 
 
181
  if(debug){
 
182
    fprintf(stderr, "Decryption of OpenPGP packet succeeded\n");
 
183
  }
 
184
 
 
185
  if (debug){
 
186
    gpgme_decrypt_result_t result;
 
187
    result = gpgme_op_decrypt_result(ctx);
 
188
    if (result == NULL){
 
189
      fprintf(stderr, "gpgme_op_decrypt_result failed\n");
 
190
    } else {
 
191
      fprintf(stderr, "Unsupported algorithm: %s\n",
 
192
              result->unsupported_algorithm);
 
193
      fprintf(stderr, "Wrong key usage: %d\n",
 
194
              result->wrong_key_usage);
 
195
      if(result->file_name != NULL){
 
196
        fprintf(stderr, "File name: %s\n", result->file_name);
 
197
      }
 
198
      gpgme_recipient_t recipient;
 
199
      recipient = result->recipients;
 
200
      if(recipient){
 
201
        while(recipient != NULL){
 
202
          fprintf(stderr, "Public key algorithm: %s\n",
 
203
                  gpgme_pubkey_algo_name(recipient->pubkey_algo));
 
204
          fprintf(stderr, "Key ID: %s\n", recipient->keyid);
 
205
          fprintf(stderr, "Secret key available: %s\n",
 
206
                  recipient->status == GPG_ERR_NO_SECKEY
 
207
                  ? "No" : "Yes");
 
208
          recipient = recipient->next;
306
209
        }
307
210
      }
308
211
    }
309
 
    goto decrypt_end;
310
212
  }
311
213
  
312
 
  if(debug){
313
 
    fprintf(stderr, "Decryption of OpenPGP data succeeded\n");
314
 
  }
 
214
  /* Delete the GPGME FILE pointer cryptotext data buffer */
 
215
  gpgme_data_release(dh_crypto);
315
216
  
316
217
  /* Seek back to the beginning of the GPGME plaintext data buffer */
317
 
  if(gpgme_data_seek(dh_plain, (off_t)0, SEEK_SET) == -1){
318
 
    perror("gpgme_data_seek");
319
 
    plaintext_length = -1;
320
 
    goto decrypt_end;
 
218
  if (gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET) == -1){
 
219
    perror("pgpme_data_seek");
321
220
  }
322
221
  
323
 
  *plaintext = NULL;
 
222
  *new_packet = 0;
324
223
  while(true){
325
 
    plaintext_capacity = adjustbuffer(plaintext,
326
 
                                      (size_t)plaintext_length,
327
 
                                      plaintext_capacity);
328
 
    if(plaintext_capacity == 0){
 
224
    new_packet_capacity = adjustbuffer(*new_packet, new_packet_length,
 
225
                                       new_packet_capacity);
 
226
    if (new_packet_capacity == 0){
329
227
        perror("adjustbuffer");
330
 
        plaintext_length = -1;
331
 
        goto decrypt_end;
 
228
        return -1;
 
229
      }
 
230
      new_packet_capacity += BUFFER_SIZE;
332
231
    }
333
232
    
334
 
    ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
 
233
    ret = gpgme_data_read(dh_plain, *new_packet + new_packet_length,
335
234
                          BUFFER_SIZE);
336
235
    /* Print the data, if any */
337
 
    if(ret == 0){
338
 
      /* EOF */
 
236
    if (ret == 0){
339
237
      break;
340
238
    }
341
239
    if(ret < 0){
342
240
      perror("gpgme_data_read");
343
 
      plaintext_length = -1;
344
 
      goto decrypt_end;
345
 
    }
346
 
    plaintext_length += ret;
347
 
  }
348
 
  
349
 
  if(debug){
350
 
    fprintf(stderr, "Decrypted password is: ");
351
 
    for(ssize_t i = 0; i < plaintext_length; i++){
352
 
      fprintf(stderr, "%02hhX ", (*plaintext)[i]);
353
 
    }
354
 
    fprintf(stderr, "\n");
355
 
  }
356
 
  
357
 
 decrypt_end:
358
 
  
359
 
  /* Delete the GPGME cryptotext data buffer */
360
 
  gpgme_data_release(dh_crypto);
 
241
      return -1;
 
242
    }
 
243
    new_packet_length += ret;
 
244
  }
 
245
 
 
246
  /* FIXME: check characters before printing to screen so to not print
 
247
     terminal control characters */
 
248
  /*   if(debug){ */
 
249
  /*     fprintf(stderr, "decrypted password is: "); */
 
250
  /*     fwrite(*new_packet, 1, new_packet_length, stderr); */
 
251
  /*     fprintf(stderr, "\n"); */
 
252
  /*   } */
361
253
  
362
254
  /* Delete the GPGME plaintext data buffer */
363
255
  gpgme_data_release(dh_plain);
364
 
  return plaintext_length;
 
256
  return new_packet_length;
365
257
}
366
258
 
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)
 
259
static const char * safer_gnutls_strerror (int value) {
 
260
  const char *ret = gnutls_strerror (value);
 
261
  if (ret == NULL)
371
262
    ret = "(unknown)";
372
263
  return ret;
373
264
}
374
265
 
375
 
/* GnuTLS log function callback */
376
266
static void debuggnutls(__attribute__((unused)) int level,
377
267
                        const char* string){
378
 
  fprintf(stderr, "GnuTLS: %s", string);
 
268
  fprintf(stderr, "%s", string);
379
269
}
380
270
 
381
 
static int init_gnutls_global(mandos_context *mc,
382
 
                              const char *pubkeyfilename,
383
 
                              const char *seckeyfilename){
 
271
static int initgnutls(mandos_context *mc){
 
272
  const char *err;
384
273
  int ret;
385
274
  
386
275
  if(debug){
387
276
    fprintf(stderr, "Initializing GnuTLS\n");
388
277
  }
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));
 
278
 
 
279
  if ((ret = gnutls_global_init ())
 
280
      != GNUTLS_E_SUCCESS) {
 
281
    fprintf (stderr, "global_init: %s\n", safer_gnutls_strerror(ret));
394
282
    return -1;
395
283
  }
396
 
  
397
 
  if(debug){
398
 
    /* "Use a log level over 10 to enable all debugging options."
399
 
     * - GnuTLS manual
400
 
     */
 
284
 
 
285
  if (debug){
401
286
    gnutls_global_set_log_level(11);
402
287
    gnutls_global_set_log_function(debuggnutls);
403
288
  }
404
289
  
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();
 
290
  /* openpgp credentials */
 
291
  if ((ret = gnutls_certificate_allocate_credentials (&es->cred))
 
292
      != GNUTLS_E_SUCCESS) {
 
293
    fprintf (stderr, "memory error: %s\n",
 
294
             safer_gnutls_strerror(ret));
414
295
    return -1;
415
296
  }
416
297
  
417
298
  if(debug){
418
 
    fprintf(stderr, "Attempting to use OpenPGP public key %s and"
419
 
            " secret key %s as GnuTLS credentials\n", pubkeyfilename,
420
 
            seckeyfilename);
 
299
    fprintf(stderr, "Attempting to use OpenPGP certificate %s"
 
300
            " and keyfile %s as GnuTLS credentials\n", certfile,
 
301
            certkey);
421
302
  }
422
303
  
423
304
  ret = gnutls_certificate_set_openpgp_key_file
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){
 
305
    (es->cred, certfile, certkey, GNUTLS_OPENPGP_FMT_BASE64);
 
306
  if (ret != GNUTLS_E_SUCCESS) {
 
307
    fprintf
 
308
      (stderr, "Error[%d] while reading the OpenPGP key pair ('%s',"
 
309
       " '%s')\n",
 
310
       ret, certfile, certkey);
 
311
    fprintf(stdout, "The Error is: %s\n",
 
312
            safer_gnutls_strerror(ret));
 
313
    return -1;
 
314
  }
 
315
  
 
316
  //GnuTLS server initialization
 
317
  if ((ret = gnutls_dh_params_init (&es->dh_params))
 
318
      != GNUTLS_E_SUCCESS) {
 
319
    fprintf (stderr, "Error in dh parameter initialization: %s\n",
 
320
             safer_gnutls_strerror(ret));
 
321
    return -1;
 
322
  }
 
323
  
 
324
  if ((ret = gnutls_dh_params_generate2 (es->dh_params, DH_BITS))
 
325
      != GNUTLS_E_SUCCESS) {
 
326
    fprintf (stderr, "Error in prime generation: %s\n",
 
327
             safer_gnutls_strerror(ret));
 
328
    return -1;
 
329
  }
 
330
  
 
331
  gnutls_certificate_set_dh_params (es->cred, es->dh_params);
 
332
  
 
333
  // GnuTLS session creation
 
334
  if ((ret = gnutls_init (&es->session, GNUTLS_SERVER))
 
335
      != GNUTLS_E_SUCCESS){
467
336
    fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
468
337
            safer_gnutls_strerror(ret));
469
338
  }
470
339
  
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
 
    }
 
340
  if ((ret = gnutls_priority_set_direct (es->session, mc->priority, &err))
 
341
      != GNUTLS_E_SUCCESS) {
 
342
    fprintf(stderr, "Syntax error at: %s\n", err);
 
343
    fprintf(stderr, "GnuTLS error: %s\n",
 
344
            safer_gnutls_strerror(ret));
 
345
    return -1;
481
346
  }
482
347
  
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",
 
348
  if ((ret = gnutls_credentials_set
 
349
       (es->session, GNUTLS_CRD_CERTIFICATE, es->cred))
 
350
      != GNUTLS_E_SUCCESS) {
 
351
    fprintf(stderr, "Error setting a credentials set: %s\n",
487
352
            safer_gnutls_strerror(ret));
488
 
    gnutls_deinit(*session);
489
353
    return -1;
490
354
  }
491
355
  
492
356
  /* ignore client certificate if any. */
493
 
  gnutls_certificate_server_set_request(*session,
494
 
                                        GNUTLS_CERT_IGNORE);
 
357
  gnutls_certificate_server_set_request (es->session,
 
358
                                         GNUTLS_CERT_IGNORE);
495
359
  
496
 
  gnutls_dh_set_prime_bits(*session, mc->dh_bits);
 
360
  gnutls_dh_set_prime_bits (es->session, DH_BITS);
497
361
  
498
362
  return 0;
499
363
}
500
364
 
501
 
/* Avahi log function callback */
502
365
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
503
366
                      __attribute__((unused)) const char *txt){}
504
367
 
505
 
/* Called when a Mandos server is found */
506
368
static int start_mandos_communication(const char *ip, uint16_t port,
507
369
                                      AvahiIfIndex if_index,
508
370
                                      mandos_context *mc){
509
371
  int ret, tcp_sd;
510
 
  ssize_t sret;
511
 
  union { struct sockaddr in; struct sockaddr_in6 in6; } to;
 
372
  struct sockaddr_in6 to;
 
373
  encrypted_session es;
512
374
  char *buffer = NULL;
513
375
  char *decrypted_buffer;
514
376
  size_t buffer_length = 0;
517
379
  size_t written;
518
380
  int retval = 0;
519
381
  char interface[IF_NAMESIZE];
520
 
  gnutls_session_t session;
521
 
  
522
 
  ret = init_gnutls_session(mc, &session);
523
 
  if(ret != 0){
524
 
    return -1;
525
 
  }
526
382
  
527
383
  if(debug){
528
 
    fprintf(stderr, "Setting up a tcp connection to %s, port %" PRIu16
529
 
            "\n", ip, port);
 
384
    fprintf(stderr, "Setting up a tcp connection to %s, port %d\n",
 
385
            ip, port);
530
386
  }
531
387
  
532
388
  tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
534
390
    perror("socket");
535
391
    return -1;
536
392
  }
537
 
  
 
393
 
538
394
  if(debug){
539
395
    if(if_indextoname((unsigned int)if_index, interface) == NULL){
540
 
      perror("if_indextoname");
 
396
      if(debug){
 
397
        perror("if_indextoname");
 
398
      }
541
399
      return -1;
542
400
    }
 
401
    
543
402
    fprintf(stderr, "Binding to interface %s\n", interface);
544
403
  }
545
404
  
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 ){
 
405
  memset(&to,0,sizeof(to));     /* Spurious warning */
 
406
  to.sin6_family = AF_INET6;
 
407
  ret = inet_pton(AF_INET6, ip, &to.sin6_addr);
 
408
  if (ret < 0 ){
552
409
    perror("inet_pton");
553
410
    return -1;
554
 
  }
 
411
  }  
555
412
  if(ret == 0){
556
413
    fprintf(stderr, "Bad address: %s\n", ip);
557
414
    return -1;
558
415
  }
559
 
  to.in6.sin6_port = htons(port); /* Spurious warnings from
560
 
                                     -Wconversion and
561
 
                                     -Wunreachable-code */
 
416
  to.sin6_port = htons(port);   /* Spurious warning */
562
417
  
563
 
  to.in6.sin6_scope_id = (uint32_t)if_index;
 
418
  to.sin6_scope_id = (uint32_t)if_index;
564
419
  
565
420
  if(debug){
566
 
    fprintf(stderr, "Connection to: %s, port %" PRIu16 "\n", ip,
567
 
            port);
568
 
    char addrstr[INET6_ADDRSTRLEN] = "";
569
 
    if(inet_ntop(to.in6.sin6_family, &(to.in6.sin6_addr), addrstr,
570
 
                 sizeof(addrstr)) == NULL){
571
 
      perror("inet_ntop");
572
 
    } else {
573
 
      if(strcmp(addrstr, ip) != 0){
574
 
        fprintf(stderr, "Canonical address form: %s\n", addrstr);
575
 
      }
576
 
    }
 
421
    fprintf(stderr, "Connection to: %s, port %d\n", ip, port);
 
422
/*     char addrstr[INET6_ADDRSTRLEN]; */
 
423
/*     if(inet_ntop(to.sin6_family, &(to.sin6_addr), addrstr, */
 
424
/*               sizeof(addrstr)) == NULL){ */
 
425
/*       perror("inet_ntop"); */
 
426
/*     } else { */
 
427
/*       fprintf(stderr, "Really connecting to: %s, port %d\n", */
 
428
/*            addrstr, ntohs(to.sin6_port)); */
 
429
/*     } */
577
430
  }
578
431
  
579
 
  ret = connect(tcp_sd, &to.in, sizeof(to));
580
 
  if(ret < 0){
 
432
  ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
 
433
  if (ret < 0){
581
434
    perror("connect");
582
435
    return -1;
583
436
  }
584
 
  
585
 
  const char *out = mandos_protocol_version;
 
437
 
 
438
  char *out = mandos_protocol_version;
586
439
  written = 0;
587
 
  while(true){
 
440
  while (true){
588
441
    size_t out_size = strlen(out);
589
 
    ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
 
442
    ret = TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
590
443
                                   out_size - written));
591
 
    if(ret == -1){
 
444
    if (ret == -1){
592
445
      perror("write");
593
446
      retval = -1;
594
 
      goto mandos_end;
 
447
      goto end;
595
448
    }
596
 
    written += (size_t)ret;
 
449
    written += ret;
597
450
    if(written < out_size){
598
451
      continue;
599
452
    } else {
600
 
      if(out == mandos_protocol_version){
 
453
      if (out == mandos_protocol_version){
601
454
        written = 0;
602
455
        out = "\r\n";
603
456
      } else {
605
458
      }
606
459
    }
607
460
  }
 
461
 
 
462
  ret = initgnutls (&es);
 
463
  if (ret != 0){
 
464
    retval = -1;
 
465
    return -1;
 
466
  }
 
467
  
 
468
  gnutls_transport_set_ptr (es.session,
 
469
                            (gnutls_transport_ptr_t) tcp_sd);
608
470
  
609
471
  if(debug){
610
472
    fprintf(stderr, "Establishing TLS session with %s\n", ip);
611
473
  }
612
474
  
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){
 
475
  ret = gnutls_handshake (es.session);
 
476
  
 
477
  if (ret != GNUTLS_E_SUCCESS){
620
478
    if(debug){
621
 
      fprintf(stderr, "*** GnuTLS Handshake failed ***\n");
622
 
      gnutls_perror(ret);
 
479
      fprintf(stderr, "\n*** Handshake failed ***\n");
 
480
      gnutls_perror (ret);
623
481
    }
624
482
    retval = -1;
625
 
    goto mandos_end;
 
483
    goto exit;
626
484
  }
627
485
  
628
 
  /* Read OpenPGP packet that contains the wanted password */
 
486
  //Retrieve OpenPGP packet that contains the wanted password
629
487
  
630
488
  if(debug){
631
489
    fprintf(stderr, "Retrieving pgp encrypted password from %s\n",
632
490
            ip);
633
491
  }
634
 
  
 
492
 
635
493
  while(true){
636
 
    buffer_capacity = adjustbuffer(&buffer, buffer_length,
637
 
                                   buffer_capacity);
638
 
    if(buffer_capacity == 0){
 
494
    buffer_capacity = adjustbuffer(buffer, buffer_length, buffer_capacity);
 
495
    if (buffer_capacity == 0){
639
496
      perror("adjustbuffer");
640
497
      retval = -1;
641
 
      goto mandos_end;
 
498
      goto exit;
642
499
    }
643
500
    
644
 
    sret = gnutls_record_recv(session, buffer+buffer_length,
645
 
                              BUFFER_SIZE);
646
 
    if(sret == 0){
 
501
    ret = gnutls_record_recv
 
502
      (es.session, buffer+buffer_length, BUFFER_SIZE);
 
503
    if (ret == 0){
647
504
      break;
648
505
    }
649
 
    if(sret < 0){
650
 
      switch(sret){
 
506
    if (ret < 0){
 
507
      switch(ret){
651
508
      case GNUTLS_E_INTERRUPTED:
652
509
      case GNUTLS_E_AGAIN:
653
510
        break;
654
511
      case GNUTLS_E_REHANDSHAKE:
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);
 
512
        ret = gnutls_handshake (es.session);
 
513
        if (ret < 0){
 
514
          fprintf(stderr, "\n*** Handshake failed ***\n");
 
515
          gnutls_perror (ret);
661
516
          retval = -1;
662
 
          goto mandos_end;
 
517
          goto exit;
663
518
        }
664
519
        break;
665
520
      default:
666
521
        fprintf(stderr, "Unknown error while reading data from"
667
 
                " encrypted session with Mandos server\n");
 
522
                " encrypted session with mandos server\n");
668
523
        retval = -1;
669
 
        gnutls_bye(session, GNUTLS_SHUT_RDWR);
670
 
        goto mandos_end;
 
524
        gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
 
525
        goto exit;
671
526
      }
672
527
    } else {
673
 
      buffer_length += (size_t) sret;
 
528
      buffer_length += (size_t) ret;
674
529
    }
675
530
  }
676
531
  
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,
 
532
  if (buffer_length > 0){
 
533
    decrypted_buffer_size = pgp_packet_decrypt(buffer,
685
534
                                               buffer_length,
686
 
                                               &decrypted_buffer);
687
 
    if(decrypted_buffer_size >= 0){
 
535
                                               &decrypted_buffer,
 
536
                                               certdir);
 
537
    if (decrypted_buffer_size >= 0){
688
538
      written = 0;
689
539
      while(written < (size_t) decrypted_buffer_size){
690
 
        ret = (int)fwrite(decrypted_buffer + written, 1,
691
 
                          (size_t)decrypted_buffer_size - written,
692
 
                          stdout);
 
540
        ret = (int)fwrite (decrypted_buffer + written, 1,
 
541
                           (size_t)decrypted_buffer_size - written,
 
542
                           stdout);
693
543
        if(ret == 0 and ferror(stdout)){
694
544
          if(debug){
695
545
            fprintf(stderr, "Error writing encrypted data: %s\n",
704
554
    } else {
705
555
      retval = -1;
706
556
    }
707
 
  } else {
708
 
    retval = -1;
709
 
  }
710
 
  
711
 
  /* Shutdown procedure */
712
 
  
713
 
 mandos_end:
 
557
  }
 
558
 
 
559
  //shutdown procedure
 
560
 
 
561
  if(debug){
 
562
    fprintf(stderr, "Closing TLS session\n");
 
563
  }
 
564
 
714
565
  free(buffer);
715
 
  ret = (int)TEMP_FAILURE_RETRY(close(tcp_sd));
716
 
  if(ret == -1){
717
 
    perror("close");
718
 
  }
719
 
  gnutls_deinit(session);
 
566
  gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
 
567
 exit:
 
568
  close(tcp_sd);
 
569
  gnutls_deinit (es.session);
 
570
  gnutls_certificate_free_credentials (es.cred);
 
571
  gnutls_global_deinit ();
720
572
  return retval;
721
573
}
722
574
 
723
 
static void resolve_callback(AvahiSServiceResolver *r,
724
 
                             AvahiIfIndex interface,
725
 
                             AVAHI_GCC_UNUSED AvahiProtocol protocol,
726
 
                             AvahiResolverEvent event,
727
 
                             const char *name,
728
 
                             const char *type,
729
 
                             const char *domain,
730
 
                             const char *host_name,
731
 
                             const AvahiAddress *address,
732
 
                             uint16_t port,
733
 
                             AVAHI_GCC_UNUSED AvahiStringList *txt,
734
 
                             AVAHI_GCC_UNUSED AvahiLookupResultFlags
735
 
                             flags,
736
 
                             void* userdata) {
 
575
static void resolve_callback( AvahiSServiceResolver *r,
 
576
                              AvahiIfIndex interface,
 
577
                              AVAHI_GCC_UNUSED AvahiProtocol protocol,
 
578
                              AvahiResolverEvent event,
 
579
                              const char *name,
 
580
                              const char *type,
 
581
                              const char *domain,
 
582
                              const char *host_name,
 
583
                              const AvahiAddress *address,
 
584
                              uint16_t port,
 
585
                              AVAHI_GCC_UNUSED AvahiStringList *txt,
 
586
                              AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
 
587
                              AVAHI_GCC_UNUSED void* userdata) {
737
588
  mandos_context *mc = userdata;
738
 
  assert(r);
 
589
  assert(r);                    /* Spurious warning */
739
590
  
740
591
  /* Called whenever a service has been resolved successfully or
741
592
     timed out */
742
593
  
743
 
  switch(event) {
 
594
  switch (event) {
744
595
  default:
745
596
  case AVAHI_RESOLVER_FAILURE:
746
 
    fprintf(stderr, "(Avahi Resolver) Failed to resolve service '%s'"
747
 
            " of type '%s' in domain '%s': %s\n", name, type, domain,
 
597
    fprintf(stderr, "(Resolver) Failed to resolve service '%s' of"
 
598
            " type '%s' in domain '%s': %s\n", name, type, domain,
748
599
            avahi_strerror(avahi_server_errno(mc->server)));
749
600
    break;
750
601
    
753
604
      char ip[AVAHI_ADDRESS_STR_MAX];
754
605
      avahi_address_snprint(ip, sizeof(ip), address);
755
606
      if(debug){
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);
 
607
        fprintf(stderr, "Mandos server \"%s\" found on %s (%s) on"
 
608
                " port %d\n", name, host_name, ip, port);
759
609
      }
760
610
      int ret = start_mandos_communication(ip, port, interface, mc);
761
 
      if(ret == 0){
762
 
        avahi_simple_poll_quit(mc->simple_poll);
 
611
      if (ret == 0){
 
612
        exit(EXIT_SUCCESS);
763
613
      }
764
614
    }
765
615
  }
773
623
                             const char *name,
774
624
                             const char *type,
775
625
                             const char *domain,
776
 
                             AVAHI_GCC_UNUSED AvahiLookupResultFlags
777
 
                             flags,
 
626
                             AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
778
627
                             void* userdata) {
779
628
  mandos_context *mc = userdata;
780
 
  assert(b);
 
629
  assert(b);                    /* Spurious warning */
781
630
  
782
631
  /* Called whenever a new services becomes available on the LAN or
783
632
     is removed from the LAN */
784
633
  
785
 
  switch(event) {
 
634
  switch (event) {
786
635
  default:
787
636
  case AVAHI_BROWSER_FAILURE:
788
 
    
789
 
    fprintf(stderr, "(Avahi browser) %s\n",
 
637
      
 
638
    fprintf(stderr, "(Browser) %s\n",
790
639
            avahi_strerror(avahi_server_errno(mc->server)));
791
640
    avahi_simple_poll_quit(mc->simple_poll);
792
641
    return;
793
 
    
 
642
      
794
643
  case AVAHI_BROWSER_NEW:
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. */
799
 
    
800
 
    if(!(avahi_s_service_resolver_new(mc->server, interface,
801
 
                                       protocol, name, type, domain,
 
644
    /* We ignore the returned resolver object. In the callback
 
645
       function we free it. If the server is terminated before
 
646
       the callback function is called the server will free
 
647
       the resolver for us. */
 
648
      
 
649
    if (!(avahi_s_service_resolver_new(mc->server, interface, protocol, name,
 
650
                                       type, domain,
802
651
                                       AVAHI_PROTO_INET6, 0,
803
652
                                       resolve_callback, mc)))
804
 
      fprintf(stderr, "Avahi: Failed to resolve service '%s': %s\n",
805
 
              name, avahi_strerror(avahi_server_errno(mc->server)));
 
653
      fprintf(stderr, "Failed to resolve service '%s': %s\n", name,
 
654
              avahi_strerror(avahi_server_errno(s)));
806
655
    break;
807
 
    
 
656
      
808
657
  case AVAHI_BROWSER_REMOVE:
809
658
    break;
810
 
    
 
659
      
811
660
  case AVAHI_BROWSER_ALL_FOR_NOW:
812
661
  case AVAHI_BROWSER_CACHE_EXHAUSTED:
813
 
    if(debug){
814
 
      fprintf(stderr, "No Mandos server found, still searching...\n");
815
 
    }
816
662
    break;
817
663
  }
818
664
}
819
665
 
820
 
int main(int argc, char *argv[]){
 
666
/* Combines file name and path and returns the malloced new
 
667
   string. some sane checks could/should be added */
 
668
static const char *combinepath(const char *first, const char *second){
 
669
  size_t f_len = strlen(first);
 
670
  size_t s_len = strlen(second);
 
671
  char *tmp = malloc(f_len + s_len + 2);
 
672
  if (tmp == NULL){
 
673
    return NULL;
 
674
  }
 
675
  if(f_len > 0){
 
676
    memcpy(tmp, first, f_len);
 
677
  }
 
678
  tmp[f_len] = '/';
 
679
  if(s_len > 0){
 
680
    memcpy(tmp + f_len + 1, second, s_len);
 
681
  }
 
682
  tmp[f_len + 1 + s_len] = '\0';
 
683
  return tmp;
 
684
}
 
685
 
 
686
 
 
687
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
 
688
    AvahiServerConfig config;
821
689
    AvahiSServiceBrowser *sb = NULL;
822
690
    int error;
823
691
    int ret;
824
 
    intmax_t tmpmax;
825
 
    int numchars;
826
 
    int exitcode = EXIT_SUCCESS;
 
692
    int returncode = EXIT_SUCCESS;
827
693
    const char *interface = "eth0";
828
694
    struct ifreq network;
829
695
    int sd;
830
 
    uid_t uid;
831
 
    gid_t gid;
832
696
    char *connect_to = NULL;
833
 
    char tempdir[] = "/tmp/mandosXXXXXX";
834
697
    AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
835
 
    const char *seckey = PATHDIR "/" SECKEY;
836
 
    const char *pubkey = PATHDIR "/" PUBKEY;
837
 
    
838
698
    mandos_context mc = { .simple_poll = NULL, .server = NULL,
839
 
                          .dh_bits = 1024, .priority = "SECURE256"
840
 
                          ":!CTYPE-X.509:+CTYPE-OPENPGP" };
841
 
    bool gnutls_initalized = false;
842
 
    bool gpgme_initalized = false;
 
699
                          .dh_bits = 2048, .priority = "SECURE256"};
843
700
    
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");
 
701
    while (true){
 
702
      static struct option long_options[] = {
 
703
        {"debug", no_argument, (int *)&debug, 1},
 
704
        {"connect", required_argument, 0, 'C'},
 
705
        {"interface", required_argument, 0, 'i'},
 
706
        {"certdir", required_argument, 0, 'd'},
 
707
        {"certkey", required_argument, 0, 'c'},
 
708
        {"certfile", required_argument, 0, 'k'},
 
709
        {"dh_bits", required_argument, 0, 'D'},
 
710
        {"priority", required_argument, 0, 'p'},
 
711
        {0, 0, 0, 0} };
 
712
      
 
713
      int option_index = 0;
 
714
      ret = getopt_long (argc, argv, "i:", long_options,
 
715
                         &option_index);
 
716
      
 
717
      if (ret == -1){
 
718
        break;
 
719
      }
 
720
      
 
721
      switch(ret){
 
722
      case 0:
 
723
        break;
 
724
      case 'i':
 
725
        interface = optarg;
 
726
        break;
 
727
      case 'C':
 
728
        connect_to = optarg;
 
729
        break;
 
730
      case 'd':
 
731
        certdir = optarg;
 
732
        break;
 
733
      case 'c':
 
734
        certfile = optarg;
 
735
        break;
 
736
      case 'k':
 
737
        certkey = optarg;
 
738
        break;
 
739
      case 'D':
 
740
        {
 
741
          long int tmp;
 
742
          errno = 0;
 
743
          tmp = strtol(optarg, NULL, 10);
 
744
          if (errno == ERANGE){
 
745
            perror("strtol");
900
746
            exit(EXIT_FAILURE);
901
747
          }
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;
 
748
          mc.dh_bits = tmp;
 
749
        }
 
750
        break;
 
751
      case 'p':
 
752
        mc.priority = optarg;
 
753
        break;
 
754
      default:
 
755
        exit(EXIT_FAILURE);
 
756
      }
 
757
    }
 
758
    
 
759
    certfile = combinepath(certdir, certfile);
 
760
    if (certfile == NULL){
 
761
      perror("combinepath");
 
762
      returncode = EXIT_FAILURE;
 
763
      goto exit;
 
764
    }
 
765
 
 
766
    certkey = combinepath(certdir, certkey);
 
767
    if (certkey == NULL){
 
768
      perror("combinepath");
 
769
      returncode = EXIT_FAILURE;
 
770
      goto exit;
993
771
    }
994
772
    
995
773
    if_index = (AvahiIfIndex) if_nametoindex(interface);
1004
782
      char *address = strrchr(connect_to, ':');
1005
783
      if(address == NULL){
1006
784
        fprintf(stderr, "No colon in address\n");
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;
 
785
        exit(EXIT_FAILURE);
 
786
      }
 
787
      errno = 0;
 
788
      uint16_t port = (uint16_t) strtol(address+1, NULL, 10);
 
789
      if(errno){
 
790
        perror("Bad port number");
 
791
        exit(EXIT_FAILURE);
 
792
      }
1019
793
      *address = '\0';
1020
794
      address = connect_to;
1021
 
      ret = start_mandos_communication(address, port, if_index, &mc);
 
795
      ret = start_mandos_communication(address, port, if_index);
1022
796
      if(ret < 0){
1023
 
        exitcode = EXIT_FAILURE;
 
797
        exit(EXIT_FAILURE);
1024
798
      } else {
1025
 
        exitcode = EXIT_SUCCESS;
1026
 
      }
1027
 
      goto end;
1028
 
    }
1029
 
    
1030
 
    if(not debug){
 
799
        exit(EXIT_SUCCESS);
 
800
      }
 
801
    }
 
802
    
 
803
    sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
 
804
    if(sd < 0) {
 
805
      perror("socket");
 
806
      returncode = EXIT_FAILURE;
 
807
      goto exit;
 
808
    }
 
809
    strcpy(network.ifr_name, interface);    
 
810
    ret = ioctl(sd, SIOCGIFFLAGS, &network);
 
811
    if(ret == -1){
 
812
      
 
813
      perror("ioctl SIOCGIFFLAGS");
 
814
      returncode = EXIT_FAILURE;
 
815
      goto exit;
 
816
    }
 
817
    if((network.ifr_flags & IFF_UP) == 0){
 
818
      network.ifr_flags |= IFF_UP;
 
819
      ret = ioctl(sd, SIOCSIFFLAGS, &network);
 
820
      if(ret == -1){
 
821
        perror("ioctl SIOCSIFFLAGS");
 
822
        returncode = EXIT_FAILURE;
 
823
        goto exit;
 
824
      }
 
825
    }
 
826
    close(sd);
 
827
    
 
828
    if (not debug){
1031
829
      avahi_set_log_function(empty_log);
1032
830
    }
1033
831
    
1034
 
    /* Initialize the pseudo-RNG for Avahi */
 
832
    /* Initialize the psuedo-RNG */
1035
833
    srand((unsigned int) time(NULL));
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",
 
834
 
 
835
    /* Allocate main loop object */
 
836
    if (!(mc.simple_poll = avahi_simple_poll_new())) {
 
837
        fprintf(stderr, "Failed to create simple poll object.\n");
 
838
        returncode = EXIT_FAILURE;      
 
839
        goto exit;
 
840
    }
 
841
 
 
842
    /* Do not publish any local records */
 
843
    avahi_server_config_init(&config);
 
844
    config.publish_hinfo = 0;
 
845
    config.publish_addresses = 0;
 
846
    config.publish_workstation = 0;
 
847
    config.publish_domain = 0;
 
848
 
 
849
    /* Allocate a new server */
 
850
    mc.server = avahi_server_new(avahi_simple_poll_get(simple_poll),
 
851
                              &config, NULL, NULL, &error);
 
852
 
 
853
    /* Free the configuration data */
 
854
    avahi_server_config_free(&config);
 
855
 
 
856
    /* Check if creating the server object succeeded */
 
857
    if (!mc.server) {
 
858
        fprintf(stderr, "Failed to create server: %s\n",
1067
859
                avahi_strerror(error));
1068
 
        exitcode = EXIT_FAILURE;
1069
 
        goto end;
 
860
        returncode = EXIT_FAILURE;
 
861
        goto exit;
1070
862
    }
1071
863
    
1072
 
    /* Create the Avahi service browser */
 
864
    /* Create the service browser */
1073
865
    sb = avahi_s_service_browser_new(mc.server, if_index,
1074
866
                                     AVAHI_PROTO_INET6,
1075
867
                                     "_mandos._tcp", NULL, 0,
1076
868
                                     browse_callback, &mc);
1077
 
    if(sb == NULL) {
 
869
    if (!sb) {
1078
870
        fprintf(stderr, "Failed to create service browser: %s\n",
1079
871
                avahi_strerror(avahi_server_errno(mc.server)));
1080
 
        exitcode = EXIT_FAILURE;
1081
 
        goto end;
 
872
        returncode = EXIT_FAILURE;
 
873
        goto exit;
1082
874
    }
1083
875
    
1084
876
    /* Run the main loop */
1085
 
    
1086
 
    if(debug){
1087
 
      fprintf(stderr, "Starting Avahi loop search\n");
 
877
 
 
878
    if (debug){
 
879
      fprintf(stderr, "Starting avahi loop search\n");
1088
880
    }
1089
881
    
1090
 
    avahi_simple_poll_loop(mc.simple_poll);
1091
 
    
1092
 
 end:
1093
 
    
1094
 
    if(debug){
 
882
    avahi_simple_poll_loop(simple_poll);
 
883
    
 
884
 exit:
 
885
 
 
886
    if (debug){
1095
887
      fprintf(stderr, "%s exiting\n", argv[0]);
1096
888
    }
1097
889
    
1098
890
    /* Cleanup things */
1099
 
    if(sb != NULL)
 
891
    if (sb)
1100
892
        avahi_s_service_browser_free(sb);
1101
893
    
1102
 
    if(mc.server != NULL)
 
894
    if (mc.server)
1103
895
        avahi_server_free(mc.server);
1104
 
    
1105
 
    if(mc.simple_poll != NULL)
1106
 
        avahi_simple_poll_free(mc.simple_poll);
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;
 
896
 
 
897
    if (simple_poll)
 
898
        avahi_simple_poll_free(simple_poll);
 
899
    free(certfile);
 
900
    free(certkey);
 
901
    
 
902
    return returncode;
1158
903
}