/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-08-03 01:09:36 UTC
  • mfrom: (24.1.9 mandos)
  • Revision ID: teddy@fukt.bsnet.se-20080803010936-ujme8tgxceszfbi1
* plugbasedclient.c (main): New "--userid" and "--groupid" options.
                            Take an additional non-option argument and
                            parse it as a plus-separated and -prefixed
                            list of additional options.

* plugins.d/mandosclient.c (DH_BITS): Replaced with
                                      "mandos_context.dh_bits".  All
                                      users changed.
  (certdir): Renamed to "keydir".  All users changed.
  (certfile): Renamed to "pubkeyfile".  All users changed.
  (certkey): Renamed to "seckeyfile".  All users changed.
  (encrypted_session): Replaced with "mandos_context".  All users
                       changed.
  (initgnutls): Take additional "session" and "dh_params" arguments.
                All callers changed.
  (start_mandos_communication): Take additional "mc" argument.  All
                                callers changed.  Print target IPv6
                                address if different than supplied
                                string.
  (simple_poll) Replaced with "mandos_context.simple_poll".  All users
                changed.
  (server): Replaced with "mandos_context.server".  All users changed.
  (main): Default interface to "eth0".  Rename "--certdir" to
          "--keydir", "--certkey" to "--seckey", and "--certfile" to
          "--pubkey".  New options "--dh-bits" and "--priority".  If
          the interface is not up, bring it up.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/***
2
 
  This file is part of avahi.
3
 
 
4
 
  avahi is free software; you can redistribute it and/or modify it
5
 
  under the terms of the GNU Lesser General Public License as
6
 
  published by the Free Software Foundation; either version 2.1 of the
7
 
  License, or (at your option) any later version.
8
 
 
9
 
  avahi is distributed in the hope that it will be useful, but WITHOUT
10
 
  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11
 
  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
12
 
  Public License for more details.
13
 
 
14
 
  You should have received a copy of the GNU Lesser General Public
15
 
  License along with avahi; if not, write to the Free Software
16
 
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
17
 
  USA.
18
 
***/
 
1
/*  -*- coding: utf-8 -*- */
 
2
/*
 
3
 * Mandos client - get and decrypt data from a Mandos server
 
4
 *
 
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>.
 
29
 */
19
30
 
 
31
/* Needed by GPGME, specifically gpgme_data_seek() */
20
32
#define _LARGEFILE_SOURCE
21
33
#define _FILE_OFFSET_BITS 64
22
34
 
25
37
#include <stdlib.h>
26
38
#include <time.h>
27
39
#include <net/if.h>             /* if_nametoindex */
 
40
#include <sys/ioctl.h>          // ioctl, ifreq, SIOCGIFFLAGS, IFF_UP, SIOCSIFFLAGS
 
41
#include <net/if.h>             // ioctl, ifreq, SIOCGIFFLAGS, IFF_UP, SIOCSIFFLAGS
28
42
 
29
43
#include <avahi-core/core.h>
30
44
#include <avahi-core/lookup.h>
34
48
#include <avahi-common/error.h>
35
49
 
36
50
//mandos client part
37
 
#include <sys/types.h>          /* socket(), setsockopt(), inet_pton() */
38
 
#include <sys/socket.h>         /* socket(), setsockopt(), struct sockaddr_in6, struct in6_addr, inet_pton() */
39
 
#include <gnutls/gnutls.h>      /* ALL GNUTLS STUFF */
40
 
#include <gnutls/openpgp.h>     /* gnutls with openpgp stuff */
 
51
#include <sys/types.h>          /* socket(), inet_pton() */
 
52
#include <sys/socket.h>         /* socket(), struct sockaddr_in6,
 
53
                                   struct in6_addr, inet_pton() */
 
54
#include <gnutls/gnutls.h>      /* All GnuTLS stuff */
 
55
#include <gnutls/openpgp.h>     /* GnuTLS with openpgp stuff */
41
56
 
42
57
#include <unistd.h>             /* close() */
43
58
#include <netinet/in.h>
50
65
#include <errno.h>              /* perror() */
51
66
#include <gpgme.h>
52
67
 
 
68
// getopt_long
 
69
#include <getopt.h>
53
70
 
54
 
#ifndef CERT_ROOT
55
 
#define CERT_ROOT "/conf/conf.d/cryptkeyreq/"
56
 
#endif
57
 
#define CERTFILE CERT_ROOT "openpgp-client.txt"
58
 
#define KEYFILE CERT_ROOT "openpgp-client-key.txt"
59
71
#define BUFFER_SIZE 256
60
 
#define DH_BITS 1024
 
72
 
 
73
static const char *keydir = "/conf/conf.d/mandos";
 
74
static const char *pubkeyfile = "pubkey.txt";
 
75
static const char *seckeyfile = "seckey.txt";
61
76
 
62
77
bool debug = false;
63
78
 
 
79
/* Used for passing in values through all the callback functions */
64
80
typedef struct {
65
 
  gnutls_session_t session;
 
81
  AvahiSimplePoll *simple_poll;
 
82
  AvahiServer *server;
66
83
  gnutls_certificate_credentials_t cred;
67
 
  gnutls_dh_params_t dh_params;
68
 
} encrypted_session;
69
 
 
70
 
 
71
 
ssize_t gpg_packet_decrypt (char *packet, size_t packet_size, char **new_packet, char *homedir){
 
84
  unsigned int dh_bits;
 
85
  const char *priority;
 
86
} mandos_context;
 
87
 
 
88
static ssize_t pgp_packet_decrypt (char *packet, size_t packet_size,
 
89
                                   char **new_packet,
 
90
                                   const char *homedir){
72
91
  gpgme_data_t dh_crypto, dh_plain;
73
92
  gpgme_ctx_t ctx;
74
93
  gpgme_error_t rc;
75
94
  ssize_t ret;
76
 
  size_t new_packet_capacity = 0;
77
 
  size_t new_packet_length = 0;
 
95
  ssize_t new_packet_capacity = 0;
 
96
  ssize_t new_packet_length = 0;
78
97
  gpgme_engine_info_t engine_info;
79
98
 
80
99
  if (debug){
81
 
    fprintf(stderr, "Attempting to decrypt password from gpg packet\n");
 
100
    fprintf(stderr, "Trying to decrypt OpenPGP packet\n");
82
101
  }
83
102
  
84
103
  /* Init GPGME */
85
104
  gpgme_check_version(NULL);
86
 
  gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
 
105
  rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
 
106
  if (rc != GPG_ERR_NO_ERROR){
 
107
    fprintf(stderr, "bad gpgme_engine_check_version: %s: %s\n",
 
108
            gpgme_strsource(rc), gpgme_strerror(rc));
 
109
    return -1;
 
110
  }
87
111
  
88
112
  /* Set GPGME home directory */
89
113
  rc = gpgme_get_engine_info (&engine_info);
129
153
    return -1;
130
154
  }
131
155
  
132
 
  /* Decrypt data from the FILE pointer to the plaintext data buffer */
 
156
  /* Decrypt data from the FILE pointer to the plaintext data
 
157
     buffer */
133
158
  rc = gpgme_op_decrypt(ctx, dh_crypto, dh_plain);
134
159
  if (rc != GPG_ERR_NO_ERROR){
135
160
    fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
138
163
  }
139
164
 
140
165
  if(debug){
141
 
    fprintf(stderr, "decryption of gpg packet succeeded\n");
 
166
    fprintf(stderr, "Decryption of OpenPGP packet succeeded\n");
142
167
  }
143
168
 
144
169
  if (debug){
147
172
    if (result == NULL){
148
173
      fprintf(stderr, "gpgme_op_decrypt_result failed\n");
149
174
    } else {
150
 
      fprintf(stderr, "Unsupported algorithm: %s\n", result->unsupported_algorithm);
151
 
      fprintf(stderr, "Wrong key usage: %d\n", result->wrong_key_usage);
 
175
      fprintf(stderr, "Unsupported algorithm: %s\n",
 
176
              result->unsupported_algorithm);
 
177
      fprintf(stderr, "Wrong key usage: %d\n",
 
178
              result->wrong_key_usage);
152
179
      if(result->file_name != NULL){
153
180
        fprintf(stderr, "File name: %s\n", result->file_name);
154
181
      }
160
187
                  gpgme_pubkey_algo_name(recipient->pubkey_algo));
161
188
          fprintf(stderr, "Key ID: %s\n", recipient->keyid);
162
189
          fprintf(stderr, "Secret key available: %s\n",
163
 
                  recipient->status == GPG_ERR_NO_SECKEY ? "No" : "Yes");
 
190
                  recipient->status == GPG_ERR_NO_SECKEY
 
191
                  ? "No" : "Yes");
164
192
          recipient = recipient->next;
165
193
        }
166
194
      }
171
199
  gpgme_data_release(dh_crypto);
172
200
  
173
201
  /* Seek back to the beginning of the GPGME plaintext data buffer */
174
 
  gpgme_data_seek(dh_plain, 0, SEEK_SET);
175
 
 
 
202
  if (gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET) == -1){
 
203
    perror("pgpme_data_seek");
 
204
  }
 
205
  
176
206
  *new_packet = 0;
177
207
  while(true){
178
208
    if (new_packet_length + BUFFER_SIZE > new_packet_capacity){
179
 
      *new_packet = realloc(*new_packet, new_packet_capacity + BUFFER_SIZE);
 
209
      *new_packet = realloc(*new_packet,
 
210
                            (unsigned int)new_packet_capacity
 
211
                            + BUFFER_SIZE);
180
212
      if (*new_packet == NULL){
181
213
        perror("realloc");
182
214
        return -1;
184
216
      new_packet_capacity += BUFFER_SIZE;
185
217
    }
186
218
    
187
 
    ret = gpgme_data_read(dh_plain, *new_packet + new_packet_length, BUFFER_SIZE);
 
219
    ret = gpgme_data_read(dh_plain, *new_packet + new_packet_length,
 
220
                          BUFFER_SIZE);
188
221
    /* Print the data, if any */
189
222
    if (ret == 0){
190
 
      /* If password is empty, then a incorrect error will be printed */
191
223
      break;
192
224
    }
193
225
    if(ret < 0){
197
229
    new_packet_length += ret;
198
230
  }
199
231
 
200
 
  if(debug){
201
 
    fprintf(stderr, "decrypted password is: %s\n", *new_packet);
202
 
  }
203
 
 
204
 
   /* Delete the GPGME plaintext data buffer */
 
232
  /* FIXME: check characters before printing to screen so to not print
 
233
     terminal control characters */
 
234
  /*   if(debug){ */
 
235
  /*     fprintf(stderr, "decrypted password is: "); */
 
236
  /*     fwrite(*new_packet, 1, new_packet_length, stderr); */
 
237
  /*     fprintf(stderr, "\n"); */
 
238
  /*   } */
 
239
  
 
240
  /* Delete the GPGME plaintext data buffer */
205
241
  gpgme_data_release(dh_plain);
206
242
  return new_packet_length;
207
243
}
213
249
  return ret;
214
250
}
215
251
 
216
 
void debuggnutls(int level, const char* string){
 
252
static void debuggnutls(__attribute__((unused)) int level,
 
253
                        const char* string){
217
254
  fprintf(stderr, "%s", string);
218
255
}
219
256
 
220
 
int initgnutls(encrypted_session *es){
 
257
static int initgnutls(mandos_context *mc, gnutls_session_t *session,
 
258
                      gnutls_dh_params_t *dh_params){
221
259
  const char *err;
222
260
  int ret;
223
 
 
 
261
  
224
262
  if(debug){
225
 
    fprintf(stderr, "Initializing gnutls\n");
 
263
    fprintf(stderr, "Initializing GnuTLS\n");
226
264
  }
227
265
 
228
 
  
229
266
  if ((ret = gnutls_global_init ())
230
267
      != GNUTLS_E_SUCCESS) {
231
268
    fprintf (stderr, "global_init: %s\n", safer_gnutls_strerror(ret));
232
269
    return -1;
233
270
  }
234
 
 
 
271
  
235
272
  if (debug){
236
273
    gnutls_global_set_log_level(11);
237
274
    gnutls_global_set_log_function(debuggnutls);
238
275
  }
239
276
  
240
 
 
241
277
  /* openpgp credentials */
242
 
  if ((ret = gnutls_certificate_allocate_credentials (&es->cred))
 
278
  if ((ret = gnutls_certificate_allocate_credentials (&mc->cred))
243
279
      != GNUTLS_E_SUCCESS) {
244
 
    fprintf (stderr, "memory error: %s\n", safer_gnutls_strerror(ret));
 
280
    fprintf (stderr, "memory error: %s\n",
 
281
             safer_gnutls_strerror(ret));
245
282
    return -1;
246
283
  }
247
 
 
 
284
  
248
285
  if(debug){
249
 
    fprintf(stderr, "Attempting to use openpgp certificate %s"
250
 
            " and keyfile %s as gnutls credentials\n", CERTFILE, KEYFILE);
 
286
    fprintf(stderr, "Attempting to use OpenPGP certificate %s"
 
287
            " and keyfile %s as GnuTLS credentials\n", pubkeyfile,
 
288
            seckeyfile);
251
289
  }
252
 
 
 
290
  
253
291
  ret = gnutls_certificate_set_openpgp_key_file
254
 
    (es->cred, CERTFILE, KEYFILE, GNUTLS_OPENPGP_FMT_BASE64);
 
292
    (mc->cred, pubkeyfile, seckeyfile, GNUTLS_OPENPGP_FMT_BASE64);
255
293
  if (ret != GNUTLS_E_SUCCESS) {
256
294
    fprintf
257
 
      (stderr, "Error[%d] while reading the OpenPGP key pair ('%s', '%s')\n",
258
 
       ret, CERTFILE, KEYFILE);
 
295
      (stderr, "Error[%d] while reading the OpenPGP key pair ('%s',"
 
296
       " '%s')\n",
 
297
       ret, pubkeyfile, seckeyfile);
259
298
    fprintf(stdout, "The Error is: %s\n",
260
299
            safer_gnutls_strerror(ret));
261
300
    return -1;
262
301
  }
263
 
 
264
 
  //Gnutls server initialization
265
 
  if ((ret = gnutls_dh_params_init (&es->dh_params))
 
302
  
 
303
  //GnuTLS server initialization
 
304
  if ((ret = gnutls_dh_params_init(dh_params))
266
305
      != GNUTLS_E_SUCCESS) {
267
306
    fprintf (stderr, "Error in dh parameter initialization: %s\n",
268
307
             safer_gnutls_strerror(ret));
269
308
    return -1;
270
309
  }
271
 
 
272
 
  if ((ret = gnutls_dh_params_generate2 (es->dh_params, DH_BITS))
 
310
  
 
311
  if ((ret = gnutls_dh_params_generate2(*dh_params, mc->dh_bits))
273
312
      != GNUTLS_E_SUCCESS) {
274
313
    fprintf (stderr, "Error in prime generation: %s\n",
275
314
             safer_gnutls_strerror(ret));
276
315
    return -1;
277
316
  }
278
 
 
279
 
  gnutls_certificate_set_dh_params (es->cred, es->dh_params);
280
 
 
281
 
  // Gnutls session creation
282
 
  if ((ret = gnutls_init (&es->session, GNUTLS_SERVER))
 
317
  
 
318
  gnutls_certificate_set_dh_params(mc->cred, *dh_params);
 
319
  
 
320
  // GnuTLS session creation
 
321
  if ((ret = gnutls_init(session, GNUTLS_SERVER))
283
322
      != GNUTLS_E_SUCCESS){
284
 
    fprintf(stderr, "Error in gnutls session initialization: %s\n",
 
323
    fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
285
324
            safer_gnutls_strerror(ret));
286
325
  }
287
 
 
288
 
  if ((ret = gnutls_priority_set_direct (es->session, "NORMAL", &err))
 
326
  
 
327
  if ((ret = gnutls_priority_set_direct(*session, mc->priority, &err))
289
328
      != GNUTLS_E_SUCCESS) {
290
329
    fprintf(stderr, "Syntax error at: %s\n", err);
291
 
    fprintf(stderr, "Gnutls error: %s\n",
 
330
    fprintf(stderr, "GnuTLS error: %s\n",
292
331
            safer_gnutls_strerror(ret));
293
332
    return -1;
294
333
  }
295
 
 
296
 
  if ((ret = gnutls_credentials_set
297
 
       (es->session, GNUTLS_CRD_CERTIFICATE, es->cred))
 
334
  
 
335
  if ((ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
 
336
                                    mc->cred))
298
337
      != GNUTLS_E_SUCCESS) {
299
338
    fprintf(stderr, "Error setting a credentials set: %s\n",
300
339
            safer_gnutls_strerror(ret));
301
340
    return -1;
302
341
  }
303
 
 
 
342
  
304
343
  /* ignore client certificate if any. */
305
 
  gnutls_certificate_server_set_request (es->session, GNUTLS_CERT_IGNORE);
 
344
  gnutls_certificate_server_set_request (*session,
 
345
                                         GNUTLS_CERT_IGNORE);
306
346
  
307
 
  gnutls_dh_set_prime_bits (es->session, DH_BITS);
 
347
  gnutls_dh_set_prime_bits (*session, mc->dh_bits);
308
348
  
309
349
  return 0;
310
350
}
311
351
 
312
 
void empty_log(AvahiLogLevel level, const char *txt){}
 
352
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
 
353
                      __attribute__((unused)) const char *txt){}
313
354
 
314
 
int start_mandos_communcation(char *ip, uint16_t port){
 
355
static int start_mandos_communication(const char *ip, uint16_t port,
 
356
                                      AvahiIfIndex if_index,
 
357
                                      mandos_context *mc){
315
358
  int ret, tcp_sd;
316
359
  struct sockaddr_in6 to;
317
 
  encrypted_session es;
318
360
  char *buffer = NULL;
319
361
  char *decrypted_buffer;
320
362
  size_t buffer_length = 0;
321
363
  size_t buffer_capacity = 0;
322
364
  ssize_t decrypted_buffer_size;
 
365
  size_t written = 0;
323
366
  int retval = 0;
324
 
  const char interface[] = "eth0";
325
 
 
 
367
  char interface[IF_NAMESIZE];
 
368
  gnutls_session_t session;
 
369
  gnutls_dh_params_t dh_params;
 
370
  
326
371
  if(debug){
327
 
    fprintf(stderr, "Setting up a tcp connection to %s\n", ip);
 
372
    fprintf(stderr, "Setting up a tcp connection to %s, port %d\n",
 
373
            ip, port);
328
374
  }
329
375
  
330
376
  tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
334
380
  }
335
381
 
336
382
  if(debug){
 
383
    if(if_indextoname((unsigned int)if_index, interface) == NULL){
 
384
      perror("if_indextoname");
 
385
      return -1;
 
386
    }
337
387
    fprintf(stderr, "Binding to interface %s\n", interface);
338
388
  }
339
 
 
340
 
  ret = setsockopt(tcp_sd, SOL_SOCKET, SO_BINDTODEVICE, interface, 5);
341
 
  if(tcp_sd < 0) {
342
 
    perror("setsockopt bindtodevice");
343
 
    return -1;
344
 
  }
345
389
  
346
 
  memset(&to,0,sizeof(to));
 
390
  memset(&to,0,sizeof(to));     /* Spurious warning */
347
391
  to.sin6_family = AF_INET6;
348
392
  ret = inet_pton(AF_INET6, ip, &to.sin6_addr);
349
393
  if (ret < 0 ){
350
394
    perror("inet_pton");
351
395
    return -1;
352
 
  }  
 
396
  }
353
397
  if(ret == 0){
354
398
    fprintf(stderr, "Bad address: %s\n", ip);
355
399
    return -1;
356
400
  }
357
 
  to.sin6_port = htons(port);
358
 
  to.sin6_scope_id = if_nametoindex(interface);
359
 
 
 
401
  to.sin6_port = htons(port);   /* Spurious warning */
 
402
  
 
403
  to.sin6_scope_id = (uint32_t)if_index;
 
404
  
360
405
  if(debug){
361
 
    fprintf(stderr, "Connection to: %s\n", ip);
 
406
    fprintf(stderr, "Connection to: %s, port %d\n", ip, port);
 
407
    char addrstr[INET6_ADDRSTRLEN] = "";
 
408
    if(inet_ntop(to.sin6_family, &(to.sin6_addr), addrstr,
 
409
                 sizeof(addrstr)) == NULL){
 
410
      perror("inet_ntop");
 
411
    } else {
 
412
      if(strcmp(addrstr, ip) != 0){
 
413
        fprintf(stderr, "Canonical address form: %s\n", addrstr);
 
414
      }
 
415
    }
362
416
  }
363
417
  
364
418
  ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
367
421
    return -1;
368
422
  }
369
423
  
370
 
  ret = initgnutls (&es);
 
424
  ret = initgnutls (mc, &session, &dh_params);
371
425
  if (ret != 0){
372
426
    retval = -1;
373
427
    return -1;
374
428
  }
375
 
    
376
 
  
377
 
  gnutls_transport_set_ptr (es.session, (gnutls_transport_ptr_t) tcp_sd);
378
 
 
 
429
  
 
430
  gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t) tcp_sd);
 
431
  
379
432
  if(debug){
380
 
    fprintf(stderr, "Establishing tls session with %s\n", ip);
 
433
    fprintf(stderr, "Establishing TLS session with %s\n", ip);
381
434
  }
382
 
 
383
435
  
384
 
  ret = gnutls_handshake (es.session);
 
436
  ret = gnutls_handshake (session);
385
437
  
386
438
  if (ret != GNUTLS_E_SUCCESS){
387
 
    fprintf(stderr, "\n*** Handshake failed ***\n");
388
 
    gnutls_perror (ret);
 
439
    if(debug){
 
440
      fprintf(stderr, "\n*** Handshake failed ***\n");
 
441
      gnutls_perror (ret);
 
442
    }
389
443
    retval = -1;
390
444
    goto exit;
391
445
  }
392
 
 
393
 
  //Retrieve gpg packet that contains the wanted password
394
 
 
 
446
  
 
447
  //Retrieve OpenPGP packet that contains the wanted password
 
448
  
395
449
  if(debug){
396
 
    fprintf(stderr, "Retrieving pgp encrypted password from %s\n", ip);
 
450
    fprintf(stderr, "Retrieving pgp encrypted password from %s\n",
 
451
            ip);
397
452
  }
398
453
 
399
454
  while(true){
406
461
      buffer_capacity += BUFFER_SIZE;
407
462
    }
408
463
    
409
 
    ret = gnutls_record_recv
410
 
      (es.session, buffer+buffer_length, BUFFER_SIZE);
 
464
    ret = gnutls_record_recv(session, buffer+buffer_length,
 
465
                             BUFFER_SIZE);
411
466
    if (ret == 0){
412
467
      break;
413
468
    }
417
472
      case GNUTLS_E_AGAIN:
418
473
        break;
419
474
      case GNUTLS_E_REHANDSHAKE:
420
 
        ret = gnutls_handshake (es.session);
 
475
        ret = gnutls_handshake (session);
421
476
        if (ret < 0){
422
477
          fprintf(stderr, "\n*** Handshake failed ***\n");
423
478
          gnutls_perror (ret);
426
481
        }
427
482
        break;
428
483
      default:
429
 
        fprintf(stderr, "Unknown error while reading data from encrypted session with mandos server\n");
 
484
        fprintf(stderr, "Unknown error while reading data from"
 
485
                " encrypted session with mandos server\n");
430
486
        retval = -1;
431
 
        gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
 
487
        gnutls_bye (session, GNUTLS_SHUT_RDWR);
432
488
        goto exit;
433
489
      }
434
490
    } else {
435
 
      buffer_length += ret;
 
491
      buffer_length += (size_t) ret;
436
492
    }
437
493
  }
438
494
  
439
495
  if (buffer_length > 0){
440
 
    if ((decrypted_buffer_size = gpg_packet_decrypt(buffer, buffer_length, &decrypted_buffer, CERT_ROOT)) >= 0){
441
 
      fwrite (decrypted_buffer, 1, decrypted_buffer_size, stdout);
 
496
    decrypted_buffer_size = pgp_packet_decrypt(buffer,
 
497
                                               buffer_length,
 
498
                                               &decrypted_buffer,
 
499
                                               keydir);
 
500
    if (decrypted_buffer_size >= 0){
 
501
      while(written < (size_t) decrypted_buffer_size){
 
502
        ret = (int)fwrite (decrypted_buffer + written, 1,
 
503
                           (size_t)decrypted_buffer_size - written,
 
504
                           stdout);
 
505
        if(ret == 0 and ferror(stdout)){
 
506
          if(debug){
 
507
            fprintf(stderr, "Error writing encrypted data: %s\n",
 
508
                    strerror(errno));
 
509
          }
 
510
          retval = -1;
 
511
          break;
 
512
        }
 
513
        written += (size_t)ret;
 
514
      }
442
515
      free(decrypted_buffer);
443
516
    } else {
444
517
      retval = -1;
448
521
  //shutdown procedure
449
522
 
450
523
  if(debug){
451
 
    fprintf(stderr, "Closing tls session\n");
 
524
    fprintf(stderr, "Closing TLS session\n");
452
525
  }
453
526
 
454
527
  free(buffer);
455
 
  gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
 
528
  gnutls_bye (session, GNUTLS_SHUT_RDWR);
456
529
 exit:
457
530
  close(tcp_sd);
458
 
  gnutls_deinit (es.session);
459
 
  gnutls_certificate_free_credentials (es.cred);
 
531
  gnutls_deinit (session);
 
532
  gnutls_certificate_free_credentials (mc->cred);
460
533
  gnutls_global_deinit ();
461
534
  return retval;
462
535
}
463
536
 
464
 
static AvahiSimplePoll *simple_poll = NULL;
465
 
static AvahiServer *server = NULL;
466
 
 
467
 
static void resolve_callback(
468
 
    AvahiSServiceResolver *r,
469
 
    AVAHI_GCC_UNUSED AvahiIfIndex interface,
470
 
    AVAHI_GCC_UNUSED AvahiProtocol protocol,
471
 
    AvahiResolverEvent event,
472
 
    const char *name,
473
 
    const char *type,
474
 
    const char *domain,
475
 
    const char *host_name,
476
 
    const AvahiAddress *address,
477
 
    uint16_t port,
478
 
    AvahiStringList *txt,
479
 
    AvahiLookupResultFlags flags,
480
 
    AVAHI_GCC_UNUSED void* userdata) {
481
 
    
482
 
    assert(r);
483
 
 
484
 
    /* Called whenever a service has been resolved successfully or timed out */
485
 
 
486
 
    switch (event) {
487
 
        case AVAHI_RESOLVER_FAILURE:
488
 
            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)));
489
 
            break;
490
 
 
491
 
        case AVAHI_RESOLVER_FOUND: {
492
 
          char ip[AVAHI_ADDRESS_STR_MAX];
493
 
            avahi_address_snprint(ip, sizeof(ip), address);
494
 
            if(debug){
495
 
              fprintf(stderr, "Mandos server found at %s on port %d\n", ip, port);
496
 
            }
497
 
            int ret = start_mandos_communcation(ip, port);
498
 
            if (ret == 0){
499
 
              exit(EXIT_SUCCESS);
500
 
            } else {
501
 
              exit(EXIT_FAILURE);
502
 
            }
503
 
        }
504
 
    }
505
 
    avahi_s_service_resolver_free(r);
506
 
}
507
 
 
508
 
static void browse_callback(
509
 
    AvahiSServiceBrowser *b,
510
 
    AvahiIfIndex interface,
511
 
    AvahiProtocol protocol,
512
 
    AvahiBrowserEvent event,
513
 
    const char *name,
514
 
    const char *type,
515
 
    const char *domain,
516
 
    AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
517
 
    void* userdata) {
518
 
    
519
 
    AvahiServer *s = userdata;
520
 
    assert(b);
521
 
 
522
 
    /* Called whenever a new services becomes available on the LAN or is removed from the LAN */
523
 
 
524
 
    switch (event) {
525
 
 
526
 
        case AVAHI_BROWSER_FAILURE:
527
 
            
528
 
            fprintf(stderr, "(Browser) %s\n", avahi_strerror(avahi_server_errno(server)));
529
 
            avahi_simple_poll_quit(simple_poll);
530
 
            return;
531
 
 
532
 
        case AVAHI_BROWSER_NEW:
533
 
            /* We ignore the returned resolver object. In the callback
534
 
               function we free it. If the server is terminated before
535
 
               the callback function is called the server will free
536
 
               the resolver for us. */
537
 
            
538
 
            if (!(avahi_s_service_resolver_new(s, interface, protocol, name, type, domain, AVAHI_PROTO_INET6, 0, resolve_callback, s)))
539
 
                fprintf(stderr, "Failed to resolve service '%s': %s\n", name, avahi_strerror(avahi_server_errno(s)));
540
 
            
541
 
            break;
542
 
 
543
 
        case AVAHI_BROWSER_REMOVE:
544
 
            break;
545
 
 
546
 
        case AVAHI_BROWSER_ALL_FOR_NOW:
547
 
        case AVAHI_BROWSER_CACHE_EXHAUSTED:
548
 
            break;
549
 
    }
550
 
}
 
537
static void resolve_callback( AvahiSServiceResolver *r,
 
538
                              AvahiIfIndex interface,
 
539
                              AVAHI_GCC_UNUSED AvahiProtocol protocol,
 
540
                              AvahiResolverEvent event,
 
541
                              const char *name,
 
542
                              const char *type,
 
543
                              const char *domain,
 
544
                              const char *host_name,
 
545
                              const AvahiAddress *address,
 
546
                              uint16_t port,
 
547
                              AVAHI_GCC_UNUSED AvahiStringList *txt,
 
548
                              AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
 
549
                              void* userdata) {
 
550
  mandos_context *mc = userdata;
 
551
  assert(r);                    /* Spurious warning */
 
552
  
 
553
  /* Called whenever a service has been resolved successfully or
 
554
     timed out */
 
555
  
 
556
  switch (event) {
 
557
  default:
 
558
  case AVAHI_RESOLVER_FAILURE:
 
559
    fprintf(stderr, "(Resolver) Failed to resolve service '%s' of"
 
560
            " type '%s' in domain '%s': %s\n", name, type, domain,
 
561
            avahi_strerror(avahi_server_errno(mc->server)));
 
562
    break;
 
563
    
 
564
  case AVAHI_RESOLVER_FOUND:
 
565
    {
 
566
      char ip[AVAHI_ADDRESS_STR_MAX];
 
567
      avahi_address_snprint(ip, sizeof(ip), address);
 
568
      if(debug){
 
569
        fprintf(stderr, "Mandos server \"%s\" found on %s (%s) on"
 
570
                " port %d\n", name, host_name, ip, port);
 
571
      }
 
572
      int ret = start_mandos_communication(ip, port, interface, mc);
 
573
      if (ret == 0){
 
574
        exit(EXIT_SUCCESS);
 
575
      }
 
576
    }
 
577
  }
 
578
  avahi_s_service_resolver_free(r);
 
579
}
 
580
 
 
581
static void browse_callback( AvahiSServiceBrowser *b,
 
582
                             AvahiIfIndex interface,
 
583
                             AvahiProtocol protocol,
 
584
                             AvahiBrowserEvent event,
 
585
                             const char *name,
 
586
                             const char *type,
 
587
                             const char *domain,
 
588
                             AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
 
589
                             void* userdata) {
 
590
  mandos_context *mc = userdata;
 
591
  assert(b);                    /* Spurious warning */
 
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(mc->server)));
 
602
    avahi_simple_poll_quit(mc->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(mc->server, interface, protocol, name,
 
612
                                       type, domain,
 
613
                                       AVAHI_PROTO_INET6, 0,
 
614
                                       resolve_callback, mc)))
 
615
      fprintf(stderr, "Failed to resolve service '%s': %s\n", name,
 
616
              avahi_strerror(avahi_server_errno(mc->server)));
 
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
}
 
627
 
 
628
/* Combines file name and path and returns the malloced new
 
629
   string. some sane checks could/should be added */
 
630
static const char *combinepath(const char *first, const char *second){
 
631
  size_t f_len = strlen(first);
 
632
  size_t s_len = strlen(second);
 
633
  char *tmp = malloc(f_len + s_len + 2);
 
634
  if (tmp == NULL){
 
635
    return NULL;
 
636
  }
 
637
  if(f_len > 0){
 
638
    memcpy(tmp, first, f_len);  /* Spurious warning */
 
639
  }
 
640
  tmp[f_len] = '/';
 
641
  if(s_len > 0){
 
642
    memcpy(tmp + f_len + 1, second, s_len); /* Spurious warning */
 
643
  }
 
644
  tmp[f_len + 1 + s_len] = '\0';
 
645
  return tmp;
 
646
}
 
647
 
551
648
 
552
649
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
553
650
    AvahiServerConfig config;
554
651
    AvahiSServiceBrowser *sb = NULL;
555
 
    const char db[] = "--debug";
556
652
    int error;
557
 
    int ret = 1;
 
653
    int ret;
 
654
    int debug_int;
558
655
    int returncode = EXIT_SUCCESS;
559
 
    char *basename = rindex(argv[0], '/');
560
 
    if(basename == NULL){
561
 
      basename = argv[0];
562
 
    } else {
563
 
      basename++;
564
 
    }
565
 
    
566
 
    char *program_name = malloc(strlen(basename) + sizeof(db));
567
 
 
568
 
    if (program_name == NULL){
569
 
      perror("argv[0]");
570
 
      return EXIT_FAILURE;
571
 
    }
572
 
    
573
 
    program_name[0] = '\0';
574
 
    
575
 
    for (int i = 1; i < argc; i++){
576
 
      if (not strncmp(argv[i], db, 5)){
577
 
          strcat(strcat(strcat(program_name, db ), "="), basename);
578
 
          if(not strcmp(argv[i], db) or not strcmp(argv[i], program_name)){
579
 
            debug = true;
580
 
          }
 
656
    const char *interface = "eth0";
 
657
    struct ifreq network;
 
658
    int sd;
 
659
    char *connect_to = NULL;
 
660
    AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
 
661
    mandos_context mc = { .simple_poll = NULL, .server = NULL,
 
662
                          .dh_bits = 1024, .priority = "SECURE256"};
 
663
    
 
664
    debug_int = debug ? 1 : 0;
 
665
    while (true){
 
666
      struct option long_options[] = {
 
667
        {"debug", no_argument, &debug_int, 1},
 
668
        {"connect", required_argument, NULL, 'c'},
 
669
        {"interface", required_argument, NULL, 'i'},
 
670
        {"keydir", required_argument, NULL, 'd'},
 
671
        {"seckey", required_argument, NULL, 's'},
 
672
        {"pubkey", required_argument, NULL, 'p'},
 
673
        {"dh-bits", required_argument, NULL, 'D'},
 
674
        {"priority", required_argument, NULL, 'P'},
 
675
        {0, 0, 0, 0} };
 
676
      
 
677
      int option_index = 0;
 
678
      ret = getopt_long (argc, argv, "i:", long_options,
 
679
                         &option_index);
 
680
      
 
681
      if (ret == -1){
 
682
        break;
 
683
      }
 
684
      
 
685
      switch(ret){
 
686
      case 0:
 
687
        break;
 
688
      case 'i':
 
689
        interface = optarg;
 
690
        break;
 
691
      case 'c':
 
692
        connect_to = optarg;
 
693
        break;
 
694
      case 'd':
 
695
        keydir = optarg;
 
696
        break;
 
697
      case 'p':
 
698
        pubkeyfile = optarg;
 
699
        break;
 
700
      case 's':
 
701
        seckeyfile = optarg;
 
702
        break;
 
703
      case 'D':
 
704
        errno = 0;
 
705
        mc.dh_bits = (unsigned int) strtol(optarg, NULL, 10);
 
706
        if (errno){
 
707
          perror("strtol");
 
708
          exit(EXIT_FAILURE);
581
709
        }
582
 
    }
583
 
    free(program_name);
584
 
 
 
710
        break;
 
711
      case 'P':
 
712
        mc.priority = optarg;
 
713
        break;
 
714
      case '?':
 
715
      default:
 
716
        exit(EXIT_FAILURE);
 
717
      }
 
718
    }
 
719
    debug = debug_int ? true : false;
 
720
    
 
721
    pubkeyfile = combinepath(keydir, pubkeyfile);
 
722
    if (pubkeyfile == NULL){
 
723
      perror("combinepath");
 
724
      returncode = EXIT_FAILURE;
 
725
      goto exit;
 
726
    }
 
727
    
 
728
    seckeyfile = combinepath(keydir, seckeyfile);
 
729
    if (seckeyfile == NULL){
 
730
      perror("combinepath");
 
731
      goto exit;
 
732
    }
 
733
    
 
734
    if_index = (AvahiIfIndex) if_nametoindex(interface);
 
735
    if(if_index == 0){
 
736
      fprintf(stderr, "No such interface: \"%s\"\n", interface);
 
737
      exit(EXIT_FAILURE);
 
738
    }
 
739
    
 
740
    if(connect_to != NULL){
 
741
      /* Connect directly, do not use Zeroconf */
 
742
      /* (Mainly meant for debugging) */
 
743
      char *address = strrchr(connect_to, ':');
 
744
      if(address == NULL){
 
745
        fprintf(stderr, "No colon in address\n");
 
746
        exit(EXIT_FAILURE);
 
747
      }
 
748
      errno = 0;
 
749
      uint16_t port = (uint16_t) strtol(address+1, NULL, 10);
 
750
      if(errno){
 
751
        perror("Bad port number");
 
752
        exit(EXIT_FAILURE);
 
753
      }
 
754
      *address = '\0';
 
755
      address = connect_to;
 
756
      ret = start_mandos_communication(address, port, if_index, &mc);
 
757
      if(ret < 0){
 
758
        exit(EXIT_FAILURE);
 
759
      } else {
 
760
        exit(EXIT_SUCCESS);
 
761
      }
 
762
    }
 
763
    
 
764
    sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
 
765
    if(sd < 0) {
 
766
      perror("socket");
 
767
      returncode = EXIT_FAILURE;
 
768
      goto exit;
 
769
    }
 
770
    strcpy(network.ifr_name, interface); /* Spurious warning */
 
771
    ret = ioctl(sd, SIOCGIFFLAGS, &network);
 
772
    if(ret == -1){
 
773
      
 
774
      perror("ioctl SIOCGIFFLAGS");
 
775
      returncode = EXIT_FAILURE;
 
776
      goto exit;
 
777
    }
 
778
    if((network.ifr_flags & IFF_UP) == 0){
 
779
      network.ifr_flags |= IFF_UP;
 
780
      ret = ioctl(sd, SIOCSIFFLAGS, &network);
 
781
      if(ret == -1){
 
782
        perror("ioctl SIOCSIFFLAGS");
 
783
        returncode = EXIT_FAILURE;
 
784
        goto exit;
 
785
      }
 
786
    }
 
787
    close(sd);
 
788
    
585
789
    if (not debug){
586
790
      avahi_set_log_function(empty_log);
587
791
    }
588
792
    
589
793
    /* Initialize the psuedo-RNG */
590
 
    srand(time(NULL));
 
794
    srand((unsigned int) time(NULL));
591
795
 
592
796
    /* Allocate main loop object */
593
 
    if (!(simple_poll = avahi_simple_poll_new())) {
 
797
    if (!(mc.simple_poll = avahi_simple_poll_new())) {
594
798
        fprintf(stderr, "Failed to create simple poll object.\n");
595
 
        
 
799
        returncode = EXIT_FAILURE;
596
800
        goto exit;
597
801
    }
598
802
 
604
808
    config.publish_domain = 0;
605
809
 
606
810
    /* Allocate a new server */
607
 
    server = avahi_server_new(avahi_simple_poll_get(simple_poll), &config, NULL, NULL, &error);
608
 
 
 
811
    mc.server=avahi_server_new(avahi_simple_poll_get(mc.simple_poll),
 
812
                               &config, NULL, NULL, &error);
 
813
    
609
814
    /* Free the configuration data */
610
815
    avahi_server_config_free(&config);
611
 
 
 
816
    
612
817
    /* Check if creating the server object succeeded */
613
 
    if (!server) {
614
 
        fprintf(stderr, "Failed to create server: %s\n", avahi_strerror(error));
 
818
    if (!mc.server) {
 
819
        fprintf(stderr, "Failed to create server: %s\n",
 
820
                avahi_strerror(error));
615
821
        returncode = EXIT_FAILURE;
616
822
        goto exit;
617
823
    }
618
824
    
619
825
    /* Create the service browser */
620
 
    if (!(sb = avahi_s_service_browser_new(server, if_nametoindex("eth0"), AVAHI_PROTO_INET6, "_mandos._tcp", NULL, 0, browse_callback, server))) {
621
 
        fprintf(stderr, "Failed to create service browser: %s\n", avahi_strerror(avahi_server_errno(server)));
 
826
    sb = avahi_s_service_browser_new(mc.server, if_index,
 
827
                                     AVAHI_PROTO_INET6,
 
828
                                     "_mandos._tcp", NULL, 0,
 
829
                                     browse_callback, &mc);
 
830
    if (!sb) {
 
831
        fprintf(stderr, "Failed to create service browser: %s\n",
 
832
                avahi_strerror(avahi_server_errno(mc.server)));
622
833
        returncode = EXIT_FAILURE;
623
834
        goto exit;
624
835
    }
629
840
      fprintf(stderr, "Starting avahi loop search\n");
630
841
    }
631
842
    
632
 
    avahi_simple_poll_loop(simple_poll);
 
843
    avahi_simple_poll_loop(mc.simple_poll);
633
844
    
634
 
exit:
 
845
 exit:
635
846
 
636
847
    if (debug){
637
848
      fprintf(stderr, "%s exiting\n", argv[0]);
641
852
    if (sb)
642
853
        avahi_s_service_browser_free(sb);
643
854
    
644
 
    if (server)
645
 
        avahi_server_free(server);
646
 
 
647
 
    if (simple_poll)
648
 
        avahi_simple_poll_free(simple_poll);
649
 
 
650
 
    return ret;
 
855
    if (mc.server)
 
856
        avahi_server_free(mc.server);
 
857
 
 
858
    if (mc.simple_poll)
 
859
        avahi_simple_poll_free(mc.simple_poll);
 
860
    free(pubkeyfile);
 
861
    free(seckeyfile);
 
862
    
 
863
    return returncode;
651
864
}