/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: Björn Påhlsson
  • Date: 2008-07-20 02:52:20 UTC
  • Revision ID: belorn@braxen-20080720025220-r5u0388uy9iu23h6
Added following support:
Pluginbased client handler
rewritten Mandos client
       Avahi instead of udp server discovery
       openpgp encrypted key support
Passprompt stand alone application for direct console input
Added logging for Mandos server

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*  -*- coding: utf-8 -*- */
2
 
/*
3
 
 * Mandos client - get and decrypt data from a Mandos server
 
1
/* $Id$ */
 
2
 
 
3
/* PLEASE NOTE *
 
4
 * This file demonstrates how to use Avahi's core API, this is
 
5
 * the embeddable mDNS stack for embedded applications.
4
6
 *
5
 
 * This program is partly derived from an example program for an Avahi
6
 
 * service browser, downloaded from
7
 
 * <http://avahi.org/browser/examples/core-browse-services.c>.  This
8
 
 * includes the following functions: "resolve_callback",
9
 
 * "browse_callback", and parts of "main".
10
 
 * 
11
 
 * Everything else is
12
 
 * Copyright © 2007-2008 Teddy Hogeborn & Björn Påhlsson
13
 
 * 
14
 
 * This program is free software: you can redistribute it and/or
15
 
 * modify it under the terms of the GNU General Public License as
16
 
 * published by the Free Software Foundation, either version 3 of the
17
 
 * License, or (at your option) any later version.
18
 
 * 
19
 
 * This program is distributed in the hope that it will be useful, but
20
 
 * WITHOUT ANY WARRANTY; without even the implied warranty of
21
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
22
 
 * General Public License for more details.
23
 
 * 
24
 
 * You should have received a copy of the GNU General Public License
25
 
 * along with this program.  If not, see
26
 
 * <http://www.gnu.org/licenses/>.
27
 
 * 
28
 
 * Contact the authors at <mandos@fukt.bsnet.se>.
 
7
 * End user applications should *not* use this API and should use
 
8
 * the D-Bus or C APIs, please see
 
9
 * client-browse-services.c and glib-integration.c
 
10
 * 
 
11
 * I repeat, you probably do *not* want to use this example.
29
12
 */
30
13
 
31
 
/* Needed by GPGME, specifically gpgme_data_seek() */
 
14
/***
 
15
  This file is part of avahi.
 
16
 
 
17
  avahi is free software; you can redistribute it and/or modify it
 
18
  under the terms of the GNU Lesser General Public License as
 
19
  published by the Free Software Foundation; either version 2.1 of the
 
20
  License, or (at your option) any later version.
 
21
 
 
22
  avahi is distributed in the hope that it will be useful, but WITHOUT
 
23
  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 
24
  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
 
25
  Public License for more details.
 
26
 
 
27
  You should have received a copy of the GNU Lesser General Public
 
28
  License along with avahi; if not, write to the Free Software
 
29
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 
30
  USA.
 
31
***/
 
32
 
32
33
#define _LARGEFILE_SOURCE
33
34
#define _FILE_OFFSET_BITS 64
34
35
 
35
 
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY() */
36
 
 
37
36
#include <stdio.h>
38
37
#include <assert.h>
39
38
#include <stdlib.h>
40
39
#include <time.h>
41
40
#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
41
 
45
42
#include <avahi-core/core.h>
46
43
#include <avahi-core/lookup.h>
50
47
#include <avahi-common/error.h>
51
48
 
52
49
//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 */
 
50
#include <sys/types.h>          /* socket(), setsockopt(), inet_pton() */
 
51
#include <sys/socket.h>         /* socket(), setsockopt(), struct sockaddr_in6, struct in6_addr, inet_pton() */
 
52
#include <gnutls/gnutls.h>      /* ALL GNUTLS STUFF */
 
53
#include <gnutls/openpgp.h>     /* gnutls with openpgp stuff */
58
54
 
59
55
#include <unistd.h>             /* close() */
60
56
#include <netinet/in.h>
67
63
#include <errno.h>              /* perror() */
68
64
#include <gpgme.h>
69
65
 
70
 
// getopt long
71
 
#include <getopt.h>
72
66
 
 
67
#ifndef CERT_ROOT
 
68
#define CERT_ROOT "/conf/conf.d/cryptkeyreq/"
 
69
#endif
 
70
#define CERTFILE CERT_ROOT "openpgp-client.txt"
 
71
#define KEYFILE CERT_ROOT "openpgp-client-key.txt"
73
72
#define BUFFER_SIZE 256
74
73
#define DH_BITS 1024
75
74
 
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";
79
 
 
80
 
bool debug = false;
81
 
 
82
 
const char mandos_protocol_version[] = "1";
83
 
 
84
75
typedef struct {
85
 
  AvahiSimplePoll *simple_poll;
86
 
  AvahiServer *server;
 
76
  gnutls_session_t session;
87
77
  gnutls_certificate_credentials_t cred;
88
 
  unsigned int dh_bits;
89
 
  const char *priority;
90
 
} mandos_context;
91
 
 
92
 
size_t adjustbuffer(char *buffer, size_t buffer_length,
93
 
                  size_t buffer_capacity){
94
 
  if (buffer_length + BUFFER_SIZE > buffer_capacity){
95
 
    buffer = realloc(buffer, buffer_capacity + BUFFER_SIZE);
96
 
    if (buffer == NULL){
97
 
      return 0;
98
 
    }
99
 
    buffer_capacity += BUFFER_SIZE;
100
 
  }
101
 
  return buffer_capacity;
102
 
}
103
 
 
104
 
static ssize_t pgp_packet_decrypt (char *packet, size_t packet_size,
105
 
                                   char **new_packet,
106
 
                                   const char *homedir){
 
78
  gnutls_dh_params_t dh_params;
 
79
} encrypted_session;
 
80
 
 
81
 
 
82
ssize_t gpg_packet_decrypt (char *packet, size_t packet_size, char **new_packet, char *homedir){
107
83
  gpgme_data_t dh_crypto, dh_plain;
108
84
  gpgme_ctx_t ctx;
109
85
  gpgme_error_t rc;
110
86
  ssize_t ret;
111
87
  size_t new_packet_capacity = 0;
112
 
  ssize_t new_packet_length = 0;
 
88
  size_t new_packet_length = 0;
113
89
  gpgme_engine_info_t engine_info;
114
90
 
115
 
  if (debug){
116
 
    fprintf(stderr, "Trying to decrypt OpenPGP packet\n");
117
 
  }
118
 
  
119
91
  /* Init GPGME */
120
92
  gpgme_check_version(NULL);
121
 
  rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
122
 
  if (rc != GPG_ERR_NO_ERROR){
123
 
    fprintf(stderr, "bad gpgme_engine_check_version: %s: %s\n",
124
 
            gpgme_strsource(rc), gpgme_strerror(rc));
125
 
    return -1;
126
 
  }
 
93
  gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
127
94
  
128
95
  /* Set GPGME home directory */
129
96
  rc = gpgme_get_engine_info (&engine_info);
169
136
    return -1;
170
137
  }
171
138
  
172
 
  /* Decrypt data from the FILE pointer to the plaintext data
173
 
     buffer */
 
139
  /* Decrypt data from the FILE pointer to the plaintext data buffer */
174
140
  rc = gpgme_op_decrypt(ctx, dh_crypto, dh_plain);
175
141
  if (rc != GPG_ERR_NO_ERROR){
176
142
    fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
177
143
            gpgme_strsource(rc), gpgme_strerror(rc));
178
144
    return -1;
179
145
  }
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;
209
 
        }
210
 
      }
211
 
    }
212
 
  }
213
146
  
 
147
/*   gpgme_decrypt_result_t result; */
 
148
/*   result = gpgme_op_decrypt_result(ctx); */
 
149
/*   fprintf(stderr, "Unsupported algorithm: %s\n", result->unsupported_algorithm); */
 
150
/*   fprintf(stderr, "Wrong key usage: %d\n", result->wrong_key_usage); */
 
151
/*   if(result->file_name != NULL){ */
 
152
/*     fprintf(stderr, "File name: %s\n", result->file_name); */
 
153
/*   } */
 
154
/*   gpgme_recipient_t recipient; */
 
155
/*   recipient = result->recipients; */
 
156
/*   if(recipient){ */
 
157
/*     while(recipient != NULL){ */
 
158
/*       fprintf(stderr, "Public key algorithm: %s\n", */
 
159
/*            gpgme_pubkey_algo_name(recipient->pubkey_algo)); */
 
160
/*       fprintf(stderr, "Key ID: %s\n", recipient->keyid); */
 
161
/*       fprintf(stderr, "Secret key available: %s\n", */
 
162
/*            recipient->status == GPG_ERR_NO_SECKEY ? "No" : "Yes"); */
 
163
/*       recipient = recipient->next; */
 
164
/*     } */
 
165
/*   } */
 
166
 
214
167
  /* Delete the GPGME FILE pointer cryptotext data buffer */
215
168
  gpgme_data_release(dh_crypto);
216
169
  
217
170
  /* Seek back to the beginning of the GPGME plaintext data buffer */
218
 
  if (gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET) == -1){
219
 
    perror("pgpme_data_seek");
220
 
  }
221
 
  
 
171
  gpgme_data_seek(dh_plain, 0, SEEK_SET);
 
172
 
222
173
  *new_packet = 0;
223
174
  while(true){
224
 
    new_packet_capacity = adjustbuffer(*new_packet, new_packet_length,
225
 
                                       new_packet_capacity);
226
 
    if (new_packet_capacity == 0){
227
 
        perror("adjustbuffer");
 
175
    if (new_packet_length + BUFFER_SIZE > new_packet_capacity){
 
176
      *new_packet = realloc(*new_packet, new_packet_capacity + BUFFER_SIZE);
 
177
      if (*new_packet == NULL){
 
178
        perror("realloc");
228
179
        return -1;
229
180
      }
230
181
      new_packet_capacity += BUFFER_SIZE;
231
182
    }
232
183
    
233
 
    ret = gpgme_data_read(dh_plain, *new_packet + new_packet_length,
234
 
                          BUFFER_SIZE);
 
184
    ret = gpgme_data_read(dh_plain, *new_packet + new_packet_length, BUFFER_SIZE);
235
185
    /* Print the data, if any */
236
186
    if (ret == 0){
 
187
      /* If password is empty, then a incorrect error will be printed */
237
188
      break;
238
189
    }
239
190
    if(ret < 0){
243
194
    new_packet_length += ret;
244
195
  }
245
196
 
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
 
  /*   } */
253
 
  
254
 
  /* Delete the GPGME plaintext data buffer */
 
197
   /* Delete the GPGME plaintext data buffer */
255
198
  gpgme_data_release(dh_plain);
256
199
  return new_packet_length;
257
200
}
263
206
  return ret;
264
207
}
265
208
 
266
 
static void debuggnutls(__attribute__((unused)) int level,
267
 
                        const char* string){
 
209
void debuggnutls(int level, const char* string){
268
210
  fprintf(stderr, "%s", string);
269
211
}
270
212
 
271
 
static int initgnutls(mandos_context *mc){
 
213
int initgnutls(encrypted_session *es){
272
214
  const char *err;
273
215
  int ret;
274
216
  
275
 
  if(debug){
276
 
    fprintf(stderr, "Initializing GnuTLS\n");
277
 
  }
278
 
 
279
217
  if ((ret = gnutls_global_init ())
280
218
      != GNUTLS_E_SUCCESS) {
281
219
    fprintf (stderr, "global_init: %s\n", safer_gnutls_strerror(ret));
282
220
    return -1;
283
221
  }
284
222
 
285
 
  if (debug){
286
 
    gnutls_global_set_log_level(11);
287
 
    gnutls_global_set_log_function(debuggnutls);
288
 
  }
289
 
  
 
223
  /* Uncomment to enable full debuggin on the gnutls library */
 
224
  /*   gnutls_global_set_log_level(11); */
 
225
  /*   gnutls_global_set_log_function(debuggnutls); */
 
226
 
 
227
 
290
228
  /* openpgp credentials */
291
229
  if ((ret = gnutls_certificate_allocate_credentials (&es->cred))
292
230
      != GNUTLS_E_SUCCESS) {
293
 
    fprintf (stderr, "memory error: %s\n",
294
 
             safer_gnutls_strerror(ret));
 
231
    fprintf (stderr, "memory error: %s\n", safer_gnutls_strerror(ret));
295
232
    return -1;
296
233
  }
297
 
  
298
 
  if(debug){
299
 
    fprintf(stderr, "Attempting to use OpenPGP certificate %s"
300
 
            " and keyfile %s as GnuTLS credentials\n", certfile,
301
 
            certkey);
302
 
  }
303
 
  
 
234
 
304
235
  ret = gnutls_certificate_set_openpgp_key_file
305
 
    (es->cred, certfile, certkey, GNUTLS_OPENPGP_FMT_BASE64);
 
236
    (es->cred, CERTFILE, KEYFILE, GNUTLS_OPENPGP_FMT_BASE64);
306
237
  if (ret != GNUTLS_E_SUCCESS) {
307
238
    fprintf
308
 
      (stderr, "Error[%d] while reading the OpenPGP key pair ('%s',"
309
 
       " '%s')\n",
310
 
       ret, certfile, certkey);
 
239
      (stderr, "Error[%d] while reading the OpenPGP key pair ('%s', '%s')\n",
 
240
       ret, CERTFILE, KEYFILE);
311
241
    fprintf(stdout, "The Error is: %s\n",
312
242
            safer_gnutls_strerror(ret));
313
243
    return -1;
314
244
  }
315
 
  
316
 
  //GnuTLS server initialization
 
245
 
 
246
  //Gnutls server initialization
317
247
  if ((ret = gnutls_dh_params_init (&es->dh_params))
318
248
      != GNUTLS_E_SUCCESS) {
319
249
    fprintf (stderr, "Error in dh parameter initialization: %s\n",
320
250
             safer_gnutls_strerror(ret));
321
251
    return -1;
322
252
  }
323
 
  
 
253
 
324
254
  if ((ret = gnutls_dh_params_generate2 (es->dh_params, DH_BITS))
325
255
      != GNUTLS_E_SUCCESS) {
326
256
    fprintf (stderr, "Error in prime generation: %s\n",
327
257
             safer_gnutls_strerror(ret));
328
258
    return -1;
329
259
  }
330
 
  
 
260
 
331
261
  gnutls_certificate_set_dh_params (es->cred, es->dh_params);
332
 
  
333
 
  // GnuTLS session creation
 
262
 
 
263
  // Gnutls session creation
334
264
  if ((ret = gnutls_init (&es->session, GNUTLS_SERVER))
335
265
      != GNUTLS_E_SUCCESS){
336
 
    fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
 
266
    fprintf(stderr, "Error in gnutls session initialization: %s\n",
337
267
            safer_gnutls_strerror(ret));
338
268
  }
339
 
  
340
 
  if ((ret = gnutls_priority_set_direct (es->session, mc->priority, &err))
 
269
 
 
270
  if ((ret = gnutls_priority_set_direct (es->session, "NORMAL", &err))
341
271
      != GNUTLS_E_SUCCESS) {
342
272
    fprintf(stderr, "Syntax error at: %s\n", err);
343
 
    fprintf(stderr, "GnuTLS error: %s\n",
 
273
    fprintf(stderr, "Gnutls error: %s\n",
344
274
            safer_gnutls_strerror(ret));
345
275
    return -1;
346
276
  }
347
 
  
 
277
 
348
278
  if ((ret = gnutls_credentials_set
349
279
       (es->session, GNUTLS_CRD_CERTIFICATE, es->cred))
350
280
      != GNUTLS_E_SUCCESS) {
352
282
            safer_gnutls_strerror(ret));
353
283
    return -1;
354
284
  }
355
 
  
 
285
 
356
286
  /* ignore client certificate if any. */
357
 
  gnutls_certificate_server_set_request (es->session,
358
 
                                         GNUTLS_CERT_IGNORE);
 
287
  gnutls_certificate_server_set_request (es->session, GNUTLS_CERT_IGNORE);
359
288
  
360
289
  gnutls_dh_set_prime_bits (es->session, DH_BITS);
361
290
  
362
291
  return 0;
363
292
}
364
293
 
365
 
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
366
 
                      __attribute__((unused)) const char *txt){}
 
294
void empty_log(AvahiLogLevel level, const char *txt){}
367
295
 
368
 
static int start_mandos_communication(const char *ip, uint16_t port,
369
 
                                      AvahiIfIndex if_index,
370
 
                                      mandos_context *mc){
 
296
int start_mandos_communcation(char *ip, uint16_t port){
371
297
  int ret, tcp_sd;
372
298
  struct sockaddr_in6 to;
 
299
  struct in6_addr ip_addr;
373
300
  encrypted_session es;
374
301
  char *buffer = NULL;
375
302
  char *decrypted_buffer;
376
303
  size_t buffer_length = 0;
377
304
  size_t buffer_capacity = 0;
378
305
  ssize_t decrypted_buffer_size;
379
 
  size_t written;
380
306
  int retval = 0;
381
 
  char interface[IF_NAMESIZE];
382
 
  
383
 
  if(debug){
384
 
    fprintf(stderr, "Setting up a tcp connection to %s, port %d\n",
385
 
            ip, port);
386
 
  }
 
307
 
387
308
  
388
309
  tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
389
310
  if(tcp_sd < 0) {
390
311
    perror("socket");
391
312
    return -1;
392
313
  }
393
 
 
394
 
  if(debug){
395
 
    if(if_indextoname((unsigned int)if_index, interface) == NULL){
396
 
      if(debug){
397
 
        perror("if_indextoname");
398
 
      }
399
 
      return -1;
400
 
    }
401
 
    
402
 
    fprintf(stderr, "Binding to interface %s\n", interface);
 
314
  
 
315
  ret = setsockopt(tcp_sd, SOL_SOCKET, SO_BINDTODEVICE, "eth0", 5);
 
316
  if(tcp_sd < 0) {
 
317
    perror("setsockopt bindtodevice");
 
318
    return -1;
403
319
  }
404
320
  
405
 
  memset(&to,0,sizeof(to));     /* Spurious warning */
 
321
  memset(&to,0,sizeof(to));
406
322
  to.sin6_family = AF_INET6;
407
 
  ret = inet_pton(AF_INET6, ip, &to.sin6_addr);
 
323
  ret = inet_pton(AF_INET6, ip, &ip_addr);
408
324
  if (ret < 0 ){
409
325
    perror("inet_pton");
410
326
    return -1;
413
329
    fprintf(stderr, "Bad address: %s\n", ip);
414
330
    return -1;
415
331
  }
416
 
  to.sin6_port = htons(port);   /* Spurious warning */
417
 
  
418
 
  to.sin6_scope_id = (uint32_t)if_index;
419
 
  
420
 
  if(debug){
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
 
/*     } */
430
 
  }
 
332
  to.sin6_port = htons(port);
 
333
  to.sin6_scope_id = if_nametoindex("eth0");
431
334
  
432
335
  ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
433
336
  if (ret < 0){
434
337
    perror("connect");
435
338
    return -1;
436
339
  }
437
 
 
438
 
  char *out = mandos_protocol_version;
439
 
  written = 0;
440
 
  while (true){
441
 
    size_t out_size = strlen(out);
442
 
    ret = TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
443
 
                                   out_size - written));
444
 
    if (ret == -1){
445
 
      perror("write");
446
 
      retval = -1;
447
 
      goto end;
448
 
    }
449
 
    written += ret;
450
 
    if(written < out_size){
451
 
      continue;
452
 
    } else {
453
 
      if (out == mandos_protocol_version){
454
 
        written = 0;
455
 
        out = "\r\n";
456
 
      } else {
457
 
        break;
458
 
      }
459
 
    }
460
 
  }
461
 
 
 
340
  
462
341
  ret = initgnutls (&es);
463
342
  if (ret != 0){
464
343
    retval = -1;
465
344
    return -1;
466
345
  }
467
 
  
468
 
  gnutls_transport_set_ptr (es.session,
469
 
                            (gnutls_transport_ptr_t) tcp_sd);
470
 
  
471
 
  if(debug){
472
 
    fprintf(stderr, "Establishing TLS session with %s\n", ip);
473
 
  }
474
 
  
 
346
    
 
347
  
 
348
  gnutls_transport_set_ptr (es.session, (gnutls_transport_ptr_t) tcp_sd);
 
349
 
475
350
  ret = gnutls_handshake (es.session);
476
351
  
477
352
  if (ret != GNUTLS_E_SUCCESS){
478
 
    if(debug){
479
 
      fprintf(stderr, "\n*** Handshake failed ***\n");
480
 
      gnutls_perror (ret);
481
 
    }
 
353
    fprintf(stderr, "\n*** Handshake failed ***\n");
 
354
    gnutls_perror (ret);
482
355
    retval = -1;
483
356
    goto exit;
484
357
  }
485
 
  
486
 
  //Retrieve OpenPGP packet that contains the wanted password
487
 
  
488
 
  if(debug){
489
 
    fprintf(stderr, "Retrieving pgp encrypted password from %s\n",
490
 
            ip);
491
 
  }
492
358
 
 
359
  //retrive password
493
360
  while(true){
494
 
    buffer_capacity = adjustbuffer(buffer, buffer_length, buffer_capacity);
495
 
    if (buffer_capacity == 0){
496
 
      perror("adjustbuffer");
497
 
      retval = -1;
498
 
      goto exit;
 
361
    if (buffer_length + BUFFER_SIZE > buffer_capacity){
 
362
      buffer = realloc(buffer, buffer_capacity + BUFFER_SIZE);
 
363
      if (buffer == NULL){
 
364
        perror("realloc");
 
365
        goto exit;
 
366
      }
 
367
      buffer_capacity += BUFFER_SIZE;
499
368
    }
500
369
    
501
370
    ret = gnutls_record_recv
518
387
        }
519
388
        break;
520
389
      default:
521
 
        fprintf(stderr, "Unknown error while reading data from"
522
 
                " encrypted session with mandos server\n");
 
390
        fprintf(stderr, "Unknown error while reading data from encrypted session with mandos server\n");
523
391
        retval = -1;
524
392
        gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
525
393
        goto exit;
526
394
      }
527
395
    } else {
528
 
      buffer_length += (size_t) ret;
 
396
      buffer_length += ret;
529
397
    }
530
398
  }
531
 
  
 
399
 
532
400
  if (buffer_length > 0){
533
 
    decrypted_buffer_size = pgp_packet_decrypt(buffer,
534
 
                                               buffer_length,
535
 
                                               &decrypted_buffer,
536
 
                                               certdir);
537
 
    if (decrypted_buffer_size >= 0){
538
 
      written = 0;
539
 
      while(written < (size_t) decrypted_buffer_size){
540
 
        ret = (int)fwrite (decrypted_buffer + written, 1,
541
 
                           (size_t)decrypted_buffer_size - written,
542
 
                           stdout);
543
 
        if(ret == 0 and ferror(stdout)){
544
 
          if(debug){
545
 
            fprintf(stderr, "Error writing encrypted data: %s\n",
546
 
                    strerror(errno));
547
 
          }
548
 
          retval = -1;
549
 
          break;
550
 
        }
551
 
        written += (size_t)ret;
552
 
      }
 
401
    if ((decrypted_buffer_size = gpg_packet_decrypt(buffer, buffer_length, &decrypted_buffer, CERT_ROOT)) == 0){
 
402
      retval = -1;
 
403
    } else {
 
404
      fwrite (decrypted_buffer, 1, decrypted_buffer_size, stdout);
553
405
      free(decrypted_buffer);
554
 
    } else {
555
 
      retval = -1;
556
406
    }
557
407
  }
558
408
 
 
409
  free(buffer);
 
410
 
559
411
  //shutdown procedure
560
 
 
561
 
  if(debug){
562
 
    fprintf(stderr, "Closing TLS session\n");
563
 
  }
564
 
 
565
 
  free(buffer);
566
412
  gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
567
413
 exit:
568
414
  close(tcp_sd);
572
418
  return retval;
573
419
}
574
420
 
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) {
588
 
  mandos_context *mc = userdata;
589
 
  assert(r);                    /* Spurious warning */
590
 
  
591
 
  /* Called whenever a service has been resolved successfully or
592
 
     timed out */
593
 
  
594
 
  switch (event) {
595
 
  default:
596
 
  case AVAHI_RESOLVER_FAILURE:
597
 
    fprintf(stderr, "(Resolver) Failed to resolve service '%s' of"
598
 
            " type '%s' in domain '%s': %s\n", name, type, domain,
599
 
            avahi_strerror(avahi_server_errno(mc->server)));
600
 
    break;
601
 
    
602
 
  case AVAHI_RESOLVER_FOUND:
603
 
    {
604
 
      char ip[AVAHI_ADDRESS_STR_MAX];
605
 
      avahi_address_snprint(ip, sizeof(ip), address);
606
 
      if(debug){
607
 
        fprintf(stderr, "Mandos server \"%s\" found on %s (%s) on"
608
 
                " port %d\n", name, host_name, ip, port);
609
 
      }
610
 
      int ret = start_mandos_communication(ip, port, interface, mc);
611
 
      if (ret == 0){
612
 
        exit(EXIT_SUCCESS);
613
 
      }
614
 
    }
615
 
  }
616
 
  avahi_s_service_resolver_free(r);
617
 
}
618
 
 
619
 
static void browse_callback( AvahiSServiceBrowser *b,
620
 
                             AvahiIfIndex interface,
621
 
                             AvahiProtocol protocol,
622
 
                             AvahiBrowserEvent event,
623
 
                             const char *name,
624
 
                             const char *type,
625
 
                             const char *domain,
626
 
                             AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
627
 
                             void* userdata) {
628
 
  mandos_context *mc = userdata;
629
 
  assert(b);                    /* Spurious warning */
630
 
  
631
 
  /* Called whenever a new services becomes available on the LAN or
632
 
     is removed from the LAN */
633
 
  
634
 
  switch (event) {
635
 
  default:
636
 
  case AVAHI_BROWSER_FAILURE:
637
 
      
638
 
    fprintf(stderr, "(Browser) %s\n",
639
 
            avahi_strerror(avahi_server_errno(mc->server)));
640
 
    avahi_simple_poll_quit(mc->simple_poll);
641
 
    return;
642
 
      
643
 
  case AVAHI_BROWSER_NEW:
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,
651
 
                                       AVAHI_PROTO_INET6, 0,
652
 
                                       resolve_callback, mc)))
653
 
      fprintf(stderr, "Failed to resolve service '%s': %s\n", name,
654
 
              avahi_strerror(avahi_server_errno(s)));
655
 
    break;
656
 
      
657
 
  case AVAHI_BROWSER_REMOVE:
658
 
    break;
659
 
      
660
 
  case AVAHI_BROWSER_ALL_FOR_NOW:
661
 
  case AVAHI_BROWSER_CACHE_EXHAUSTED:
662
 
    break;
663
 
  }
664
 
}
665
 
 
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
 
 
 
421
static AvahiSimplePoll *simple_poll = NULL;
 
422
static AvahiServer *server = NULL;
 
423
 
 
424
static void resolve_callback(
 
425
    AvahiSServiceResolver *r,
 
426
    AVAHI_GCC_UNUSED AvahiIfIndex interface,
 
427
    AVAHI_GCC_UNUSED AvahiProtocol protocol,
 
428
    AvahiResolverEvent event,
 
429
    const char *name,
 
430
    const char *type,
 
431
    const char *domain,
 
432
    const char *host_name,
 
433
    const AvahiAddress *address,
 
434
    uint16_t port,
 
435
    AvahiStringList *txt,
 
436
    AvahiLookupResultFlags flags,
 
437
    AVAHI_GCC_UNUSED void* userdata) {
 
438
    
 
439
    assert(r);
 
440
 
 
441
    /* Called whenever a service has been resolved successfully or timed out */
 
442
 
 
443
    switch (event) {
 
444
        case AVAHI_RESOLVER_FAILURE:
 
445
            fprintf(stderr, "(Resolver) Failed to resolve service '%s' of type '%s' in domain '%s': %s\n", name, type, domain, avahi_strerror(avahi_server_errno(server)));
 
446
            break;
 
447
 
 
448
        case AVAHI_RESOLVER_FOUND: {
 
449
          char ip[AVAHI_ADDRESS_STR_MAX];
 
450
            avahi_address_snprint(ip, sizeof(ip), address);
 
451
            int ret = start_mandos_communcation(ip, port);
 
452
            if (ret == 0){
 
453
              exit(EXIT_SUCCESS);
 
454
            } else {
 
455
              exit(EXIT_FAILURE);
 
456
            }
 
457
        }
 
458
    }
 
459
    avahi_s_service_resolver_free(r);
 
460
}
 
461
 
 
462
static void browse_callback(
 
463
    AvahiSServiceBrowser *b,
 
464
    AvahiIfIndex interface,
 
465
    AvahiProtocol protocol,
 
466
    AvahiBrowserEvent event,
 
467
    const char *name,
 
468
    const char *type,
 
469
    const char *domain,
 
470
    AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
 
471
    void* userdata) {
 
472
    
 
473
    AvahiServer *s = userdata;
 
474
    assert(b);
 
475
 
 
476
    /* Called whenever a new services becomes available on the LAN or is removed from the LAN */
 
477
 
 
478
    switch (event) {
 
479
 
 
480
        case AVAHI_BROWSER_FAILURE:
 
481
            
 
482
            fprintf(stderr, "(Browser) %s\n", avahi_strerror(avahi_server_errno(server)));
 
483
            avahi_simple_poll_quit(simple_poll);
 
484
            return;
 
485
 
 
486
        case AVAHI_BROWSER_NEW:
 
487
            /* We ignore the returned resolver object. In the callback
 
488
               function we free it. If the server is terminated before
 
489
               the callback function is called the server will free
 
490
               the resolver for us. */
 
491
            
 
492
            if (!(avahi_s_service_resolver_new(s, interface, protocol, name, type, domain, AVAHI_PROTO_INET6, 0, resolve_callback, s)))
 
493
                fprintf(stderr, "Failed to resolve service '%s': %s\n", name, avahi_strerror(avahi_server_errno(s)));
 
494
            
 
495
            break;
 
496
 
 
497
        case AVAHI_BROWSER_REMOVE:
 
498
            break;
 
499
 
 
500
        case AVAHI_BROWSER_ALL_FOR_NOW:
 
501
        case AVAHI_BROWSER_CACHE_EXHAUSTED:
 
502
            break;
 
503
    }
 
504
}
686
505
 
687
506
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
688
507
    AvahiServerConfig config;
689
508
    AvahiSServiceBrowser *sb = NULL;
690
509
    int error;
691
 
    int ret;
692
 
    int returncode = EXIT_SUCCESS;
693
 
    const char *interface = "eth0";
694
 
    struct ifreq network;
695
 
    int sd;
696
 
    char *connect_to = NULL;
697
 
    AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
698
 
    mandos_context mc = { .simple_poll = NULL, .server = NULL,
699
 
                          .dh_bits = 2048, .priority = "SECURE256"};
700
 
    
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");
746
 
            exit(EXIT_FAILURE);
747
 
          }
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
 
    }
 
510
    int ret = 1;
765
511
 
766
 
    certkey = combinepath(certdir, certkey);
767
 
    if (certkey == NULL){
768
 
      perror("combinepath");
769
 
      returncode = EXIT_FAILURE;
770
 
      goto exit;
771
 
    }
772
 
    
773
 
    if_index = (AvahiIfIndex) if_nametoindex(interface);
774
 
    if(if_index == 0){
775
 
      fprintf(stderr, "No such interface: \"%s\"\n", interface);
776
 
      exit(EXIT_FAILURE);
777
 
    }
778
 
    
779
 
    if(connect_to != NULL){
780
 
      /* Connect directly, do not use Zeroconf */
781
 
      /* (Mainly meant for debugging) */
782
 
      char *address = strrchr(connect_to, ':');
783
 
      if(address == NULL){
784
 
        fprintf(stderr, "No colon in address\n");
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
 
      }
793
 
      *address = '\0';
794
 
      address = connect_to;
795
 
      ret = start_mandos_communication(address, port, if_index);
796
 
      if(ret < 0){
797
 
        exit(EXIT_FAILURE);
798
 
      } else {
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){
829
 
      avahi_set_log_function(empty_log);
830
 
    }
 
512
    avahi_set_log_function(empty_log);
831
513
    
832
514
    /* Initialize the psuedo-RNG */
833
 
    srand((unsigned int) time(NULL));
 
515
    srand(time(NULL));
834
516
 
835
517
    /* Allocate main loop object */
836
 
    if (!(mc.simple_poll = avahi_simple_poll_new())) {
 
518
    if (!(simple_poll = avahi_simple_poll_new())) {
837
519
        fprintf(stderr, "Failed to create simple poll object.\n");
838
 
        returncode = EXIT_FAILURE;      
839
 
        goto exit;
 
520
        goto fail;
840
521
    }
841
522
 
842
523
    /* Do not publish any local records */
846
527
    config.publish_workstation = 0;
847
528
    config.publish_domain = 0;
848
529
 
 
530
/*     /\* Set a unicast DNS server for wide area DNS-SD *\/ */
 
531
/*     avahi_address_parse("193.11.177.11", AVAHI_PROTO_UNSPEC, &config.wide_area_servers[0]); */
 
532
/*     config.n_wide_area_servers = 1; */
 
533
/*     config.enable_wide_area = 1; */
 
534
    
849
535
    /* Allocate a new server */
850
 
    mc.server = avahi_server_new(avahi_simple_poll_get(simple_poll),
851
 
                              &config, NULL, NULL, &error);
 
536
    server = avahi_server_new(avahi_simple_poll_get(simple_poll), &config, NULL, NULL, &error);
852
537
 
853
538
    /* Free the configuration data */
854
539
    avahi_server_config_free(&config);
855
540
 
856
 
    /* Check if creating the server object succeeded */
857
 
    if (!mc.server) {
858
 
        fprintf(stderr, "Failed to create server: %s\n",
859
 
                avahi_strerror(error));
860
 
        returncode = EXIT_FAILURE;
861
 
        goto exit;
 
541
    /* Check wether creating the server object succeeded */
 
542
    if (!server) {
 
543
        fprintf(stderr, "Failed to create server: %s\n", avahi_strerror(error));
 
544
        goto fail;
862
545
    }
863
546
    
864
547
    /* Create the service browser */
865
 
    sb = avahi_s_service_browser_new(mc.server, if_index,
866
 
                                     AVAHI_PROTO_INET6,
867
 
                                     "_mandos._tcp", NULL, 0,
868
 
                                     browse_callback, &mc);
869
 
    if (!sb) {
870
 
        fprintf(stderr, "Failed to create service browser: %s\n",
871
 
                avahi_strerror(avahi_server_errno(mc.server)));
872
 
        returncode = EXIT_FAILURE;
873
 
        goto exit;
 
548
    if (!(sb = avahi_s_service_browser_new(server, if_nametoindex("eth0"), AVAHI_PROTO_INET6, "_mandos._tcp", NULL, 0, browse_callback, server))) {
 
549
        fprintf(stderr, "Failed to create service browser: %s\n", avahi_strerror(avahi_server_errno(server)));
 
550
        goto fail;
874
551
    }
875
552
    
876
553
    /* Run the main loop */
877
 
 
878
 
    if (debug){
879
 
      fprintf(stderr, "Starting avahi loop search\n");
880
 
    }
881
 
    
882
554
    avahi_simple_poll_loop(simple_poll);
883
555
    
884
 
 exit:
885
 
 
886
 
    if (debug){
887
 
      fprintf(stderr, "%s exiting\n", argv[0]);
888
 
    }
 
556
    ret = 0;
 
557
    
 
558
fail:
889
559
    
890
560
    /* Cleanup things */
891
561
    if (sb)
892
562
        avahi_s_service_browser_free(sb);
893
563
    
894
 
    if (mc.server)
895
 
        avahi_server_free(mc.server);
 
564
    if (server)
 
565
        avahi_server_free(server);
896
566
 
897
567
    if (simple_poll)
898
568
        avahi_simple_poll_free(simple_poll);
899
 
    free(certfile);
900
 
    free(certkey);
901
 
    
902
 
    return returncode;
 
569
 
 
570
    return ret;
903
571
}