/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

  • Committer: Teddy Hogeborn
  • Date: 2008-07-21 15:34:44 UTC
  • Revision ID: teddy@fukt.bsnet.se-20080721153444-lugbjkj1oq65ugq3
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
                     $(LANGUAGE).
  (WARN, DEBUG, COVERAGE, LANGUAGE): New.
  (LDFLAGS): New; use $(COVERAGE)

* plugbasedclient.c: Added copyright header.
  (process.buffer_size, process.buffer_length): Changed to "size_t".
  (main): Cast arguments to malloc and realloc.  Detect read errors
          from processes.

* mandosclient.c: Added copyright header.
  (interface): Moved to inside "main".
  (gpg_packet_decrypt): Renamed to "pgp_packet_decrypt"; all callers
                        changed.  Changed "new_packet_capacity" and
                        "new_packet_length" to be ssize_t.  Cast
                        arguments to realloc.
  (debuggnutls): Attribute "level" argument as unused.
  (empty_log): Attribute "level" and "txt" arguments as unused.
  (start_mandos_communication): New argument "if_index".  Bug fix:
                                check ret, no tcp_sd, for errors from
                                setsockopt.  Use "if_index" directly
                                instead of looking up the index.  Loop
                                around fwrite until all data is written.
  (resolve_callback): Attribute "txt", and "flags" as usused.  Added
                      default case to switch.  Also show server host
                      name.  Call start_mandos_communication with
                      "interface".
  (browse_callback): Added default case to switch.
  (main): Variable "interface" moved here.  Cast "srand" argument.
          Bug fix: Call avahi_s_service_browser_new with index of
          "interface", not "eth0".

* passprompt.c: Added copyright header.
  (termination_handler): Attribute "signum" argument as unused.

Show diffs side-by-side

added added

removed removed

Lines of Context:
8
8
 * includes the following functions: "resolve_callback",
9
9
 * "browse_callback", and parts of "main".
10
10
 * 
11
 
 * Everything else is
12
 
 * Copyright © 2007-2008 Teddy Hogeborn & Björn Påhlsson
 
11
 * Everything else is Copyright © 2007-2008 Teddy Hogeborn and Björn
 
12
 * Påhlsson.
13
13
 * 
14
14
 * This program is free software: you can redistribute it and/or
15
15
 * modify it under the terms of the GNU General Public License as
25
25
 * along with this program.  If not, see
26
26
 * <http://www.gnu.org/licenses/>.
27
27
 * 
28
 
 * Contact the authors at <mandos@fukt.bsnet.se>.
 
28
 * Contact the authors at <https://www.fukt.bsnet.se/~belorn/> and
 
29
 * <https://www.fukt.bsnet.se/~teddy/>.
29
30
 */
30
31
 
31
 
/* Needed by GPGME, specifically gpgme_data_seek() */
 
32
#define _FORTIFY_SOURCE 2
 
33
 
32
34
#define _LARGEFILE_SOURCE
33
35
#define _FILE_OFFSET_BITS 64
34
36
 
37
39
#include <stdlib.h>
38
40
#include <time.h>
39
41
#include <net/if.h>             /* if_nametoindex */
40
 
#include <sys/ioctl.h>          /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
41
 
                                   SIOCSIFFLAGS */
42
 
#include <net/if.h>             /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
43
 
                                   SIOCSIFFLAGS */
44
42
 
45
43
#include <avahi-core/core.h>
46
44
#include <avahi-core/lookup.h>
50
48
#include <avahi-common/error.h>
51
49
 
52
50
//mandos client part
53
 
#include <sys/types.h>          /* socket(), inet_pton() */
54
 
#include <sys/socket.h>         /* socket(), struct sockaddr_in6,
 
51
#include <sys/types.h>          /* socket(), setsockopt(),
 
52
                                   inet_pton() */
 
53
#include <sys/socket.h>         /* socket(), setsockopt(),
 
54
                                   struct sockaddr_in6,
55
55
                                   struct in6_addr, inet_pton() */
56
56
#include <gnutls/gnutls.h>      /* All GnuTLS stuff */
57
57
#include <gnutls/openpgp.h>     /* GnuTLS with openpgp stuff */
67
67
#include <errno.h>              /* perror() */
68
68
#include <gpgme.h>
69
69
 
70
 
// getopt_long
 
70
// getopt long
71
71
#include <getopt.h>
72
72
 
 
73
#ifndef CERT_ROOT
 
74
#define CERT_ROOT "/conf/conf.d/cryptkeyreq/"
 
75
#endif
 
76
#define CERTFILE CERT_ROOT "openpgp-client.txt"
 
77
#define KEYFILE CERT_ROOT "openpgp-client-key.txt"
73
78
#define BUFFER_SIZE 256
74
 
 
75
 
static const char *keydir = "/conf/conf.d/mandos";
76
 
static const char *pubkeyfile = "pubkey.txt";
77
 
static const char *seckeyfile = "seckey.txt";
 
79
#define DH_BITS 1024
78
80
 
79
81
bool debug = false;
80
82
 
81
 
/* Used for passing in values through all the callback functions */
82
83
typedef struct {
83
 
  AvahiSimplePoll *simple_poll;
84
 
  AvahiServer *server;
 
84
  gnutls_session_t session;
85
85
  gnutls_certificate_credentials_t cred;
86
 
  unsigned int dh_bits;
87
 
  const char *priority;
88
 
} mandos_context;
89
 
 
90
 
/* 
91
 
 * Decrypt OpenPGP data using keyrings in HOMEDIR.
92
 
 * Returns -1 on error
93
 
 */
94
 
static ssize_t pgp_packet_decrypt (const char *cryptotext,
95
 
                                   size_t crypto_size,
96
 
                                   char **plaintext,
97
 
                                   const char *homedir){
 
86
  gnutls_dh_params_t dh_params;
 
87
} encrypted_session;
 
88
 
 
89
 
 
90
ssize_t pgp_packet_decrypt (char *packet, size_t packet_size,
 
91
                            char **new_packet, const char *homedir){
98
92
  gpgme_data_t dh_crypto, dh_plain;
99
93
  gpgme_ctx_t ctx;
100
94
  gpgme_error_t rc;
101
95
  ssize_t ret;
102
 
  ssize_t plaintext_capacity = 0;
103
 
  ssize_t plaintext_length = 0;
 
96
  ssize_t new_packet_capacity = 0;
 
97
  ssize_t new_packet_length = 0;
104
98
  gpgme_engine_info_t engine_info;
105
 
  
 
99
 
106
100
  if (debug){
107
 
    fprintf(stderr, "Trying to decrypt OpenPGP data\n");
 
101
    fprintf(stderr, "Trying to decrypt OpenPGP packet\n");
108
102
  }
109
103
  
110
104
  /* Init GPGME */
111
105
  gpgme_check_version(NULL);
112
 
  rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
113
 
  if (rc != GPG_ERR_NO_ERROR){
114
 
    fprintf(stderr, "bad gpgme_engine_check_version: %s: %s\n",
115
 
            gpgme_strsource(rc), gpgme_strerror(rc));
116
 
    return -1;
117
 
  }
 
106
  gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
118
107
  
119
 
  /* Set GPGME home directory for the OpenPGP engine only */
 
108
  /* Set GPGME home directory */
120
109
  rc = gpgme_get_engine_info (&engine_info);
121
110
  if (rc != GPG_ERR_NO_ERROR){
122
111
    fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
132
121
    engine_info = engine_info->next;
133
122
  }
134
123
  if(engine_info == NULL){
135
 
    fprintf(stderr, "Could not set GPGME home dir to %s\n", homedir);
 
124
    fprintf(stderr, "Could not set home dir to %s\n", homedir);
136
125
    return -1;
137
126
  }
138
127
  
139
 
  /* Create new GPGME data buffer from memory cryptotext */
140
 
  rc = gpgme_data_new_from_mem(&dh_crypto, cryptotext, crypto_size,
141
 
                               0);
 
128
  /* Create new GPGME data buffer from packet buffer */
 
129
  rc = gpgme_data_new_from_mem(&dh_crypto, packet, packet_size, 0);
142
130
  if (rc != GPG_ERR_NO_ERROR){
143
131
    fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
144
132
            gpgme_strsource(rc), gpgme_strerror(rc));
150
138
  if (rc != GPG_ERR_NO_ERROR){
151
139
    fprintf(stderr, "bad gpgme_data_new: %s: %s\n",
152
140
            gpgme_strsource(rc), gpgme_strerror(rc));
153
 
    gpgme_data_release(dh_crypto);
154
141
    return -1;
155
142
  }
156
143
  
159
146
  if (rc != GPG_ERR_NO_ERROR){
160
147
    fprintf(stderr, "bad gpgme_new: %s: %s\n",
161
148
            gpgme_strsource(rc), gpgme_strerror(rc));
162
 
    plaintext_length = -1;
163
 
    goto decrypt_end;
 
149
    return -1;
164
150
  }
165
151
  
166
 
  /* Decrypt data from the cryptotext data buffer to the plaintext
167
 
     data buffer */
 
152
  /* Decrypt data from the FILE pointer to the plaintext data
 
153
     buffer */
168
154
  rc = gpgme_op_decrypt(ctx, dh_crypto, dh_plain);
169
155
  if (rc != GPG_ERR_NO_ERROR){
170
156
    fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
171
157
            gpgme_strsource(rc), gpgme_strerror(rc));
172
 
    plaintext_length = -1;
173
 
    goto decrypt_end;
 
158
    return -1;
174
159
  }
175
 
  
 
160
 
176
161
  if(debug){
177
 
    fprintf(stderr, "Decryption of OpenPGP data succeeded\n");
 
162
    fprintf(stderr, "Decryption of OpenPGP packet succeeded\n");
178
163
  }
179
 
  
 
164
 
180
165
  if (debug){
181
166
    gpgme_decrypt_result_t result;
182
167
    result = gpgme_op_decrypt_result(ctx);
206
191
    }
207
192
  }
208
193
  
 
194
  /* Delete the GPGME FILE pointer cryptotext data buffer */
 
195
  gpgme_data_release(dh_crypto);
 
196
  
209
197
  /* 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");
212
 
    plaintext_length = -1;
213
 
    goto decrypt_end;
214
 
  }
215
 
  
216
 
  *plaintext = NULL;
 
198
  gpgme_data_seek(dh_plain, 0, SEEK_SET);
 
199
 
 
200
  *new_packet = 0;
217
201
  while(true){
218
 
    if (plaintext_length + BUFFER_SIZE > plaintext_capacity){
219
 
      *plaintext = realloc(*plaintext,
220
 
                            (unsigned int)plaintext_capacity
 
202
    if (new_packet_length + BUFFER_SIZE > new_packet_capacity){
 
203
      *new_packet = realloc(*new_packet,
 
204
                            (unsigned int)new_packet_capacity
221
205
                            + BUFFER_SIZE);
222
 
      if (*plaintext == NULL){
 
206
      if (*new_packet == NULL){
223
207
        perror("realloc");
224
 
        plaintext_length = -1;
225
 
        goto decrypt_end;
 
208
        return -1;
226
209
      }
227
 
      plaintext_capacity += BUFFER_SIZE;
 
210
      new_packet_capacity += BUFFER_SIZE;
228
211
    }
229
212
    
230
 
    ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
 
213
    ret = gpgme_data_read(dh_plain, *new_packet + new_packet_length,
231
214
                          BUFFER_SIZE);
232
215
    /* Print the data, if any */
233
216
    if (ret == 0){
234
 
      /* EOF */
235
217
      break;
236
218
    }
237
219
    if(ret < 0){
238
220
      perror("gpgme_data_read");
239
 
      plaintext_length = -1;
240
 
      goto decrypt_end;
 
221
      return -1;
241
222
    }
242
 
    plaintext_length += ret;
 
223
    new_packet_length += ret;
243
224
  }
244
225
 
245
 
  if(debug){
246
 
    fprintf(stderr, "Decrypted password is: ");
247
 
    for(size_t i = 0; i < plaintext_length; i++){
248
 
      fprintf(stderr, "%02hhX ", (*plaintext)[i]);
249
 
    }
250
 
    fprintf(stderr, "\n");
251
 
  }
252
 
  
253
 
 decrypt_end:
254
 
  
255
 
  /* Delete the GPGME cryptotext data buffer */
256
 
  gpgme_data_release(dh_crypto);
 
226
  /* FIXME: check characters before printing to screen so to not print
 
227
     terminal control characters */
 
228
  /*   if(debug){ */
 
229
  /*     fprintf(stderr, "decrypted password is: "); */
 
230
  /*     fwrite(*new_packet, 1, new_packet_length, stderr); */
 
231
  /*     fprintf(stderr, "\n"); */
 
232
  /*   } */
257
233
  
258
234
  /* Delete the GPGME plaintext data buffer */
259
235
  gpgme_data_release(dh_plain);
260
 
  return plaintext_length;
 
236
  return new_packet_length;
261
237
}
262
238
 
263
239
static const char * safer_gnutls_strerror (int value) {
267
243
  return ret;
268
244
}
269
245
 
270
 
static void debuggnutls(__attribute__((unused)) int level,
271
 
                        const char* string){
 
246
void debuggnutls(__attribute__((unused)) int level,
 
247
                 const char* string){
272
248
  fprintf(stderr, "%s", string);
273
249
}
274
250
 
275
 
static int initgnutls(mandos_context *mc, gnutls_session_t *session,
276
 
                      gnutls_dh_params_t *dh_params){
 
251
int initgnutls(encrypted_session *es){
277
252
  const char *err;
278
253
  int ret;
279
254
  
280
255
  if(debug){
281
256
    fprintf(stderr, "Initializing GnuTLS\n");
282
257
  }
283
 
 
 
258
  
284
259
  if ((ret = gnutls_global_init ())
285
260
      != GNUTLS_E_SUCCESS) {
286
261
    fprintf (stderr, "global_init: %s\n", safer_gnutls_strerror(ret));
287
262
    return -1;
288
263
  }
289
 
  
 
264
 
290
265
  if (debug){
291
266
    gnutls_global_set_log_level(11);
292
267
    gnutls_global_set_log_function(debuggnutls);
293
268
  }
294
269
  
295
270
  /* openpgp credentials */
296
 
  if ((ret = gnutls_certificate_allocate_credentials (&mc->cred))
 
271
  if ((ret = gnutls_certificate_allocate_credentials (&es->cred))
297
272
      != GNUTLS_E_SUCCESS) {
298
273
    fprintf (stderr, "memory error: %s\n",
299
274
             safer_gnutls_strerror(ret));
302
277
  
303
278
  if(debug){
304
279
    fprintf(stderr, "Attempting to use OpenPGP certificate %s"
305
 
            " and keyfile %s as GnuTLS credentials\n", pubkeyfile,
306
 
            seckeyfile);
 
280
            " and keyfile %s as GnuTLS credentials\n", CERTFILE,
 
281
            KEYFILE);
307
282
  }
308
283
  
309
284
  ret = gnutls_certificate_set_openpgp_key_file
310
 
    (mc->cred, pubkeyfile, seckeyfile, GNUTLS_OPENPGP_FMT_BASE64);
 
285
    (es->cred, CERTFILE, KEYFILE, GNUTLS_OPENPGP_FMT_BASE64);
311
286
  if (ret != GNUTLS_E_SUCCESS) {
312
287
    fprintf
313
288
      (stderr, "Error[%d] while reading the OpenPGP key pair ('%s',"
314
289
       " '%s')\n",
315
 
       ret, pubkeyfile, seckeyfile);
 
290
       ret, CERTFILE, KEYFILE);
316
291
    fprintf(stdout, "The Error is: %s\n",
317
292
            safer_gnutls_strerror(ret));
318
293
    return -1;
319
294
  }
320
295
  
321
296
  //GnuTLS server initialization
322
 
  if ((ret = gnutls_dh_params_init(dh_params))
 
297
  if ((ret = gnutls_dh_params_init (&es->dh_params))
323
298
      != GNUTLS_E_SUCCESS) {
324
299
    fprintf (stderr, "Error in dh parameter initialization: %s\n",
325
300
             safer_gnutls_strerror(ret));
326
301
    return -1;
327
302
  }
328
303
  
329
 
  if ((ret = gnutls_dh_params_generate2(*dh_params, mc->dh_bits))
 
304
  if ((ret = gnutls_dh_params_generate2 (es->dh_params, DH_BITS))
330
305
      != GNUTLS_E_SUCCESS) {
331
306
    fprintf (stderr, "Error in prime generation: %s\n",
332
307
             safer_gnutls_strerror(ret));
333
308
    return -1;
334
309
  }
335
310
  
336
 
  gnutls_certificate_set_dh_params(mc->cred, *dh_params);
 
311
  gnutls_certificate_set_dh_params (es->cred, es->dh_params);
337
312
  
338
313
  // GnuTLS session creation
339
 
  if ((ret = gnutls_init(session, GNUTLS_SERVER))
 
314
  if ((ret = gnutls_init (&es->session, GNUTLS_SERVER))
340
315
      != GNUTLS_E_SUCCESS){
341
316
    fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
342
317
            safer_gnutls_strerror(ret));
343
318
  }
344
319
  
345
 
  if ((ret = gnutls_priority_set_direct(*session, mc->priority, &err))
 
320
  if ((ret = gnutls_priority_set_direct (es->session, "NORMAL", &err))
346
321
      != GNUTLS_E_SUCCESS) {
347
322
    fprintf(stderr, "Syntax error at: %s\n", err);
348
323
    fprintf(stderr, "GnuTLS error: %s\n",
350
325
    return -1;
351
326
  }
352
327
  
353
 
  if ((ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
354
 
                                    mc->cred))
 
328
  if ((ret = gnutls_credentials_set
 
329
       (es->session, GNUTLS_CRD_CERTIFICATE, es->cred))
355
330
      != GNUTLS_E_SUCCESS) {
356
331
    fprintf(stderr, "Error setting a credentials set: %s\n",
357
332
            safer_gnutls_strerror(ret));
359
334
  }
360
335
  
361
336
  /* ignore client certificate if any. */
362
 
  gnutls_certificate_server_set_request (*session,
 
337
  gnutls_certificate_server_set_request (es->session,
363
338
                                         GNUTLS_CERT_IGNORE);
364
339
  
365
 
  gnutls_dh_set_prime_bits (*session, mc->dh_bits);
 
340
  gnutls_dh_set_prime_bits (es->session, DH_BITS);
366
341
  
367
342
  return 0;
368
343
}
369
344
 
370
 
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
371
 
                      __attribute__((unused)) const char *txt){}
 
345
void empty_log(__attribute__((unused)) AvahiLogLevel level,
 
346
               __attribute__((unused)) const char *txt){}
372
347
 
373
 
static int start_mandos_communication(const char *ip, uint16_t port,
374
 
                                      AvahiIfIndex if_index,
375
 
                                      mandos_context *mc){
 
348
int start_mandos_communication(char *ip, uint16_t port,
 
349
                               unsigned int if_index){
376
350
  int ret, tcp_sd;
377
351
  struct sockaddr_in6 to;
 
352
  encrypted_session es;
378
353
  char *buffer = NULL;
379
354
  char *decrypted_buffer;
380
355
  size_t buffer_length = 0;
381
356
  size_t buffer_capacity = 0;
382
357
  ssize_t decrypted_buffer_size;
383
 
  size_t written = 0;
384
358
  int retval = 0;
385
359
  char interface[IF_NAMESIZE];
386
 
  gnutls_session_t session;
387
 
  gnutls_dh_params_t dh_params;
388
360
  
389
361
  if(debug){
390
 
    fprintf(stderr, "Setting up a tcp connection to %s, port %d\n",
391
 
            ip, port);
 
362
    fprintf(stderr, "Setting up a tcp connection to %s\n", ip);
392
363
  }
393
364
  
394
365
  tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
396
367
    perror("socket");
397
368
    return -1;
398
369
  }
399
 
 
400
 
  if(debug){
401
 
    if(if_indextoname((unsigned int)if_index, interface) == NULL){
 
370
  
 
371
  if(if_indextoname(if_index, interface) == NULL){
 
372
    if(debug){
402
373
      perror("if_indextoname");
403
 
      return -1;
404
374
    }
 
375
    return -1;
 
376
  }
 
377
  
 
378
  if(debug){
405
379
    fprintf(stderr, "Binding to interface %s\n", interface);
406
380
  }
407
381
  
408
 
  memset(&to,0,sizeof(to));     /* Spurious warning */
 
382
  ret = setsockopt(tcp_sd, SOL_SOCKET, SO_BINDTODEVICE, interface, 5);
 
383
  if(ret < 0) {
 
384
    perror("setsockopt bindtodevice");
 
385
    return -1;
 
386
  }
 
387
  
 
388
  memset(&to,0,sizeof(to));
409
389
  to.sin6_family = AF_INET6;
410
390
  ret = inet_pton(AF_INET6, ip, &to.sin6_addr);
411
391
  if (ret < 0 ){
412
392
    perror("inet_pton");
413
393
    return -1;
414
 
  }
 
394
  }  
415
395
  if(ret == 0){
416
396
    fprintf(stderr, "Bad address: %s\n", ip);
417
397
    return -1;
418
398
  }
419
 
  to.sin6_port = htons(port);   /* Spurious warning */
 
399
  /* Spurious warnings for the next line, see for instance
 
400
     <http://bugs.debian.org/488884> */
 
401
  to.sin6_port = htons(port);
420
402
  
421
403
  to.sin6_scope_id = (uint32_t)if_index;
422
404
  
423
405
  if(debug){
424
 
    fprintf(stderr, "Connection to: %s, port %d\n", ip, port);
425
 
    char addrstr[INET6_ADDRSTRLEN] = "";
426
 
    if(inet_ntop(to.sin6_family, &(to.sin6_addr), addrstr,
427
 
                 sizeof(addrstr)) == NULL){
428
 
      perror("inet_ntop");
429
 
    } else {
430
 
      if(strcmp(addrstr, ip) != 0){
431
 
        fprintf(stderr, "Canonical address form: %s\n", addrstr);
432
 
      }
433
 
    }
 
406
    fprintf(stderr, "Connection to: %s\n", ip);
434
407
  }
435
408
  
436
409
  ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
439
412
    return -1;
440
413
  }
441
414
  
442
 
  ret = initgnutls (mc, &session, &dh_params);
 
415
  ret = initgnutls (&es);
443
416
  if (ret != 0){
444
417
    retval = -1;
445
418
    return -1;
446
419
  }
447
420
  
448
 
  gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t) tcp_sd);
 
421
  gnutls_transport_set_ptr (es.session,
 
422
                            (gnutls_transport_ptr_t) tcp_sd);
449
423
  
450
424
  if(debug){
451
425
    fprintf(stderr, "Establishing TLS session with %s\n", ip);
452
426
  }
453
427
  
454
 
  ret = gnutls_handshake (session);
 
428
  ret = gnutls_handshake (es.session);
455
429
  
456
430
  if (ret != GNUTLS_E_SUCCESS){
457
 
    if(debug){
458
 
      fprintf(stderr, "\n*** Handshake failed ***\n");
459
 
      gnutls_perror (ret);
460
 
    }
 
431
    fprintf(stderr, "\n*** Handshake failed ***\n");
 
432
    gnutls_perror (ret);
461
433
    retval = -1;
462
434
    goto exit;
463
435
  }
479
451
      buffer_capacity += BUFFER_SIZE;
480
452
    }
481
453
    
482
 
    ret = gnutls_record_recv(session, buffer+buffer_length,
483
 
                             BUFFER_SIZE);
 
454
    ret = gnutls_record_recv
 
455
      (es.session, buffer+buffer_length, BUFFER_SIZE);
484
456
    if (ret == 0){
485
457
      break;
486
458
    }
490
462
      case GNUTLS_E_AGAIN:
491
463
        break;
492
464
      case GNUTLS_E_REHANDSHAKE:
493
 
        ret = gnutls_handshake (session);
 
465
        ret = gnutls_handshake (es.session);
494
466
        if (ret < 0){
495
467
          fprintf(stderr, "\n*** Handshake failed ***\n");
496
468
          gnutls_perror (ret);
502
474
        fprintf(stderr, "Unknown error while reading data from"
503
475
                " encrypted session with mandos server\n");
504
476
        retval = -1;
505
 
        gnutls_bye (session, GNUTLS_SHUT_RDWR);
 
477
        gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
506
478
        goto exit;
507
479
      }
508
480
    } else {
514
486
    decrypted_buffer_size = pgp_packet_decrypt(buffer,
515
487
                                               buffer_length,
516
488
                                               &decrypted_buffer,
517
 
                                               keydir);
 
489
                                               CERT_ROOT);
518
490
    if (decrypted_buffer_size >= 0){
519
 
      while(written < (size_t) decrypted_buffer_size){
520
 
        ret = (int)fwrite (decrypted_buffer + written, 1,
521
 
                           (size_t)decrypted_buffer_size - written,
522
 
                           stdout);
 
491
      while(decrypted_buffer_size > 0){
 
492
        ret = fwrite (decrypted_buffer, 1, (size_t)decrypted_buffer_size,
 
493
                      stdout);
523
494
        if(ret == 0 and ferror(stdout)){
524
495
          if(debug){
525
496
            fprintf(stderr, "Error writing encrypted data: %s\n",
528
499
          retval = -1;
529
500
          break;
530
501
        }
531
 
        written += (size_t)ret;
 
502
        decrypted_buffer += ret;
 
503
        decrypted_buffer_size -= ret;
532
504
      }
533
505
      free(decrypted_buffer);
534
506
    } else {
543
515
  }
544
516
 
545
517
  free(buffer);
546
 
  gnutls_bye (session, GNUTLS_SHUT_RDWR);
 
518
  gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
547
519
 exit:
548
520
  close(tcp_sd);
549
 
  gnutls_deinit (session);
550
 
  gnutls_certificate_free_credentials (mc->cred);
 
521
  gnutls_deinit (es.session);
 
522
  gnutls_certificate_free_credentials (es.cred);
551
523
  gnutls_global_deinit ();
552
524
  return retval;
553
525
}
554
526
 
555
 
static void resolve_callback(AvahiSServiceResolver *r,
556
 
                             AvahiIfIndex interface,
557
 
                             AVAHI_GCC_UNUSED AvahiProtocol protocol,
558
 
                             AvahiResolverEvent event,
559
 
                             const char *name,
560
 
                             const char *type,
561
 
                             const char *domain,
562
 
                             const char *host_name,
563
 
                             const AvahiAddress *address,
564
 
                             uint16_t port,
565
 
                             AVAHI_GCC_UNUSED AvahiStringList *txt,
566
 
                             AVAHI_GCC_UNUSED AvahiLookupResultFlags
567
 
                             flags,
568
 
                             void* userdata) {
569
 
  mandos_context *mc = userdata;
570
 
  assert(r);                    /* Spurious warning */
571
 
  
 
527
static AvahiSimplePoll *simple_poll = NULL;
 
528
static AvahiServer *server = NULL;
 
529
 
 
530
static void resolve_callback(
 
531
    AvahiSServiceResolver *r,
 
532
    AVAHI_GCC_UNUSED AvahiIfIndex interface,
 
533
    AVAHI_GCC_UNUSED AvahiProtocol protocol,
 
534
    AvahiResolverEvent event,
 
535
    const char *name,
 
536
    const char *type,
 
537
    const char *domain,
 
538
    const char *host_name,
 
539
    const AvahiAddress *address,
 
540
    uint16_t port,
 
541
    AVAHI_GCC_UNUSED AvahiStringList *txt,
 
542
    AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
 
543
    AVAHI_GCC_UNUSED void* userdata) {
 
544
    
 
545
  assert(r);
 
546
 
572
547
  /* Called whenever a service has been resolved successfully or
573
548
     timed out */
574
 
  
 
549
 
575
550
  switch (event) {
576
551
  default:
577
552
  case AVAHI_RESOLVER_FAILURE:
578
553
    fprintf(stderr, "(Resolver) Failed to resolve service '%s' of"
579
554
            " type '%s' in domain '%s': %s\n", name, type, domain,
580
 
            avahi_strerror(avahi_server_errno(mc->server)));
 
555
            avahi_strerror(avahi_server_errno(server)));
581
556
    break;
582
 
    
 
557
      
583
558
  case AVAHI_RESOLVER_FOUND:
584
559
    {
585
560
      char ip[AVAHI_ADDRESS_STR_MAX];
586
561
      avahi_address_snprint(ip, sizeof(ip), address);
587
562
      if(debug){
588
 
        fprintf(stderr, "Mandos server \"%s\" found on %s (%s) on"
589
 
                " port %d\n", name, host_name, ip, port);
 
563
        fprintf(stderr, "Mandos server found on %s (%s) on port %d\n",
 
564
                host_name, ip, port);
590
565
      }
591
 
      int ret = start_mandos_communication(ip, port, interface, mc);
 
566
      int ret = start_mandos_communication(ip, port,
 
567
                                           (unsigned int)
 
568
                                           interface);
592
569
      if (ret == 0){
593
570
        exit(EXIT_SUCCESS);
 
571
      } else {
 
572
        exit(EXIT_FAILURE);
594
573
      }
595
574
    }
596
575
  }
597
576
  avahi_s_service_resolver_free(r);
598
577
}
599
578
 
600
 
static void browse_callback( AvahiSServiceBrowser *b,
601
 
                             AvahiIfIndex interface,
602
 
                             AvahiProtocol protocol,
603
 
                             AvahiBrowserEvent event,
604
 
                             const char *name,
605
 
                             const char *type,
606
 
                             const char *domain,
607
 
                             AVAHI_GCC_UNUSED AvahiLookupResultFlags
608
 
                             flags,
609
 
                             void* userdata) {
610
 
  mandos_context *mc = userdata;
611
 
  assert(b);                    /* Spurious warning */
612
 
  
613
 
  /* Called whenever a new services becomes available on the LAN or
614
 
     is removed from the LAN */
615
 
  
616
 
  switch (event) {
617
 
  default:
618
 
  case AVAHI_BROWSER_FAILURE:
619
 
    
620
 
    fprintf(stderr, "(Browser) %s\n",
621
 
            avahi_strerror(avahi_server_errno(mc->server)));
622
 
    avahi_simple_poll_quit(mc->simple_poll);
623
 
    return;
624
 
    
625
 
  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. */
630
 
    
631
 
    if (!(avahi_s_service_resolver_new(mc->server, interface,
632
 
                                       protocol, name, type, domain,
633
 
                                       AVAHI_PROTO_INET6, 0,
634
 
                                       resolve_callback, mc)))
635
 
      fprintf(stderr, "Failed to resolve service '%s': %s\n", name,
636
 
              avahi_strerror(avahi_server_errno(mc->server)));
637
 
    break;
638
 
    
639
 
  case AVAHI_BROWSER_REMOVE:
640
 
    break;
641
 
    
642
 
  case AVAHI_BROWSER_ALL_FOR_NOW:
643
 
  case AVAHI_BROWSER_CACHE_EXHAUSTED:
644
 
    break;
645
 
  }
646
 
}
647
 
 
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
 
 
 
579
static void browse_callback(
 
580
    AvahiSServiceBrowser *b,
 
581
    AvahiIfIndex interface,
 
582
    AvahiProtocol protocol,
 
583
    AvahiBrowserEvent event,
 
584
    const char *name,
 
585
    const char *type,
 
586
    const char *domain,
 
587
    AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
 
588
    void* userdata) {
 
589
    
 
590
    AvahiServer *s = userdata;
 
591
    assert(b);
 
592
 
 
593
    /* Called whenever a new services becomes available on the LAN or
 
594
       is removed from the LAN */
 
595
 
 
596
    switch (event) {
 
597
    default:
 
598
    case AVAHI_BROWSER_FAILURE:
 
599
      
 
600
      fprintf(stderr, "(Browser) %s\n",
 
601
              avahi_strerror(avahi_server_errno(server)));
 
602
      avahi_simple_poll_quit(simple_poll);
 
603
      return;
 
604
      
 
605
    case AVAHI_BROWSER_NEW:
 
606
      /* We ignore the returned resolver object. In the callback
 
607
         function we free it. If the server is terminated before
 
608
         the callback function is called the server will free
 
609
         the resolver for us. */
 
610
      
 
611
      if (!(avahi_s_service_resolver_new(s, interface, protocol, name,
 
612
                                         type, domain,
 
613
                                         AVAHI_PROTO_INET6, 0,
 
614
                                         resolve_callback, s)))
 
615
        fprintf(stderr, "Failed to resolve service '%s': %s\n", name,
 
616
                avahi_strerror(avahi_server_errno(s)));
 
617
      break;
 
618
      
 
619
    case AVAHI_BROWSER_REMOVE:
 
620
      break;
 
621
      
 
622
    case AVAHI_BROWSER_ALL_FOR_NOW:
 
623
    case AVAHI_BROWSER_CACHE_EXHAUSTED:
 
624
      break;
 
625
    }
 
626
}
668
627
 
669
628
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
670
629
    AvahiServerConfig config;
671
630
    AvahiSServiceBrowser *sb = NULL;
672
631
    int error;
673
632
    int ret;
674
 
    int debug_int;
675
633
    int returncode = EXIT_SUCCESS;
676
634
    const char *interface = "eth0";
677
 
    struct ifreq network;
678
 
    int sd;
679
 
    char *connect_to = NULL;
680
 
    AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
681
 
    mandos_context mc = { .simple_poll = NULL, .server = NULL,
682
 
                          .dh_bits = 1024, .priority = "SECURE256"};
683
635
    
684
 
    debug_int = debug ? 1 : 0;
685
636
    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'},
 
637
      static struct option long_options[] = {
 
638
        {"debug", no_argument, (int *)&debug, 1},
 
639
        {"interface", required_argument, 0, 'i'},
695
640
        {0, 0, 0, 0} };
696
 
      
 
641
 
697
642
      int option_index = 0;
698
643
      ret = getopt_long (argc, argv, "i:", long_options,
699
644
                         &option_index);
700
 
      
 
645
 
701
646
      if (ret == -1){
702
647
        break;
703
648
      }
708
653
      case 'i':
709
654
        interface = optarg;
710
655
        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
656
      default:
736
657
        exit(EXIT_FAILURE);
737
658
      }
738
659
    }
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;
752
 
    }
753
 
    
754
 
    if_index = (AvahiIfIndex) if_nametoindex(interface);
755
 
    if(if_index == 0){
756
 
      fprintf(stderr, "No such interface: \"%s\"\n", interface);
757
 
      exit(EXIT_FAILURE);
758
 
    }
759
 
    
760
 
    if(connect_to != NULL){
761
 
      /* Connect directly, do not use Zeroconf */
762
 
      /* (Mainly meant for debugging) */
763
 
      char *address = strrchr(connect_to, ':');
764
 
      if(address == NULL){
765
 
        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
 
      }
774
 
      *address = '\0';
775
 
      address = connect_to;
776
 
      ret = start_mandos_communication(address, port, if_index, &mc);
777
 
      if(ret < 0){
778
 
        exit(EXIT_FAILURE);
779
 
      } 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
660
    
809
661
    if (not debug){
810
662
      avahi_set_log_function(empty_log);
814
666
    srand((unsigned int) time(NULL));
815
667
 
816
668
    /* Allocate main loop object */
817
 
    if (!(mc.simple_poll = avahi_simple_poll_new())) {
 
669
    if (!(simple_poll = avahi_simple_poll_new())) {
818
670
        fprintf(stderr, "Failed to create simple poll object.\n");
819
 
        returncode = EXIT_FAILURE;
 
671
        
820
672
        goto exit;
821
673
    }
822
674
 
828
680
    config.publish_domain = 0;
829
681
 
830
682
    /* Allocate a new server */
831
 
    mc.server=avahi_server_new(avahi_simple_poll_get(mc.simple_poll),
832
 
                               &config, NULL, NULL, &error);
833
 
    
 
683
    server = avahi_server_new(avahi_simple_poll_get(simple_poll),
 
684
                              &config, NULL, NULL, &error);
 
685
 
834
686
    /* Free the configuration data */
835
687
    avahi_server_config_free(&config);
836
 
    
 
688
 
837
689
    /* Check if creating the server object succeeded */
838
 
    if (!mc.server) {
 
690
    if (!server) {
839
691
        fprintf(stderr, "Failed to create server: %s\n",
840
692
                avahi_strerror(error));
841
693
        returncode = EXIT_FAILURE;
843
695
    }
844
696
    
845
697
    /* Create the service browser */
846
 
    sb = avahi_s_service_browser_new(mc.server, if_index,
 
698
    sb = avahi_s_service_browser_new(server,
 
699
                                     (AvahiIfIndex)
 
700
                                     if_nametoindex(interface),
847
701
                                     AVAHI_PROTO_INET6,
848
702
                                     "_mandos._tcp", NULL, 0,
849
 
                                     browse_callback, &mc);
 
703
                                     browse_callback, server);
850
704
    if (!sb) {
851
705
        fprintf(stderr, "Failed to create service browser: %s\n",
852
 
                avahi_strerror(avahi_server_errno(mc.server)));
 
706
                avahi_strerror(avahi_server_errno(server)));
853
707
        returncode = EXIT_FAILURE;
854
708
        goto exit;
855
709
    }
860
714
      fprintf(stderr, "Starting avahi loop search\n");
861
715
    }
862
716
    
863
 
    avahi_simple_poll_loop(mc.simple_poll);
 
717
    avahi_simple_poll_loop(simple_poll);
864
718
    
865
719
 exit:
866
720
 
872
726
    if (sb)
873
727
        avahi_s_service_browser_free(sb);
874
728
    
875
 
    if (mc.server)
876
 
        avahi_server_free(mc.server);
877
 
 
878
 
    if (mc.simple_poll)
879
 
        avahi_simple_poll_free(mc.simple_poll);
880
 
    free(pubkeyfile);
881
 
    free(seckeyfile);
882
 
    
 
729
    if (server)
 
730
        avahi_server_free(server);
 
731
 
 
732
    if (simple_poll)
 
733
        avahi_simple_poll_free(simple_poll);
 
734
 
883
735
    return returncode;
884
736
}