/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
 
/* $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.
 
1
/*  -*- coding: utf-8 -*- */
 
2
/*
 
3
 * Mandos client - get and decrypt data from a Mandos server
6
4
 *
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.
 
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>.
12
29
 */
13
30
 
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
 
 
 
31
/* Needed by GPGME, specifically gpgme_data_seek() */
33
32
#define _LARGEFILE_SOURCE
34
33
#define _FILE_OFFSET_BITS 64
35
34
 
38
37
#include <stdlib.h>
39
38
#include <time.h>
40
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
41
42
 
42
43
#include <avahi-core/core.h>
43
44
#include <avahi-core/lookup.h>
47
48
#include <avahi-common/error.h>
48
49
 
49
50
//mandos client part
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 */
 
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 */
54
56
 
55
57
#include <unistd.h>             /* close() */
56
58
#include <netinet/in.h>
63
65
#include <errno.h>              /* perror() */
64
66
#include <gpgme.h>
65
67
 
 
68
// getopt_long
 
69
#include <getopt.h>
66
70
 
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"
72
71
#define BUFFER_SIZE 256
73
 
#define DH_BITS 1024
74
 
 
 
72
 
 
73
static const char *keydir = "/conf/conf.d/mandos";
 
74
static const char *pubkeyfile = "pubkey.txt";
 
75
static const char *seckeyfile = "seckey.txt";
 
76
 
 
77
bool debug = false;
 
78
 
 
79
/* Used for passing in values through all the callback functions */
75
80
typedef struct {
76
 
  gnutls_session_t session;
 
81
  AvahiSimplePoll *simple_poll;
 
82
  AvahiServer *server;
77
83
  gnutls_certificate_credentials_t cred;
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){
 
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){
83
91
  gpgme_data_t dh_crypto, dh_plain;
84
92
  gpgme_ctx_t ctx;
85
93
  gpgme_error_t rc;
86
94
  ssize_t ret;
87
 
  size_t new_packet_capacity = 0;
88
 
  size_t new_packet_length = 0;
 
95
  ssize_t new_packet_capacity = 0;
 
96
  ssize_t new_packet_length = 0;
89
97
  gpgme_engine_info_t engine_info;
90
98
 
 
99
  if (debug){
 
100
    fprintf(stderr, "Trying to decrypt OpenPGP packet\n");
 
101
  }
 
102
  
91
103
  /* Init GPGME */
92
104
  gpgme_check_version(NULL);
93
 
  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
  }
94
111
  
95
112
  /* Set GPGME home directory */
96
113
  rc = gpgme_get_engine_info (&engine_info);
136
153
    return -1;
137
154
  }
138
155
  
139
 
  /* Decrypt data from the FILE pointer to the plaintext data buffer */
 
156
  /* Decrypt data from the FILE pointer to the plaintext data
 
157
     buffer */
140
158
  rc = gpgme_op_decrypt(ctx, dh_crypto, dh_plain);
141
159
  if (rc != GPG_ERR_NO_ERROR){
142
160
    fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
143
161
            gpgme_strsource(rc), gpgme_strerror(rc));
144
162
    return -1;
145
163
  }
 
164
 
 
165
  if(debug){
 
166
    fprintf(stderr, "Decryption of OpenPGP packet succeeded\n");
 
167
  }
 
168
 
 
169
  if (debug){
 
170
    gpgme_decrypt_result_t result;
 
171
    result = gpgme_op_decrypt_result(ctx);
 
172
    if (result == NULL){
 
173
      fprintf(stderr, "gpgme_op_decrypt_result failed\n");
 
174
    } else {
 
175
      fprintf(stderr, "Unsupported algorithm: %s\n",
 
176
              result->unsupported_algorithm);
 
177
      fprintf(stderr, "Wrong key usage: %d\n",
 
178
              result->wrong_key_usage);
 
179
      if(result->file_name != NULL){
 
180
        fprintf(stderr, "File name: %s\n", result->file_name);
 
181
      }
 
182
      gpgme_recipient_t recipient;
 
183
      recipient = result->recipients;
 
184
      if(recipient){
 
185
        while(recipient != NULL){
 
186
          fprintf(stderr, "Public key algorithm: %s\n",
 
187
                  gpgme_pubkey_algo_name(recipient->pubkey_algo));
 
188
          fprintf(stderr, "Key ID: %s\n", recipient->keyid);
 
189
          fprintf(stderr, "Secret key available: %s\n",
 
190
                  recipient->status == GPG_ERR_NO_SECKEY
 
191
                  ? "No" : "Yes");
 
192
          recipient = recipient->next;
 
193
        }
 
194
      }
 
195
    }
 
196
  }
146
197
  
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
 
 
167
198
  /* Delete the GPGME FILE pointer cryptotext data buffer */
168
199
  gpgme_data_release(dh_crypto);
169
200
  
170
201
  /* Seek back to the beginning of the GPGME plaintext data buffer */
171
 
  gpgme_data_seek(dh_plain, 0, SEEK_SET);
172
 
 
 
202
  if (gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET) == -1){
 
203
    perror("pgpme_data_seek");
 
204
  }
 
205
  
173
206
  *new_packet = 0;
174
207
  while(true){
175
208
    if (new_packet_length + BUFFER_SIZE > new_packet_capacity){
176
 
      *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);
177
212
      if (*new_packet == NULL){
178
213
        perror("realloc");
179
214
        return -1;
181
216
      new_packet_capacity += BUFFER_SIZE;
182
217
    }
183
218
    
184
 
    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);
185
221
    /* Print the data, if any */
186
222
    if (ret == 0){
187
 
      /* If password is empty, then a incorrect error will be printed */
188
223
      break;
189
224
    }
190
225
    if(ret < 0){
194
229
    new_packet_length += ret;
195
230
  }
196
231
 
197
 
   /* 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 */
198
241
  gpgme_data_release(dh_plain);
199
242
  return new_packet_length;
200
243
}
206
249
  return ret;
207
250
}
208
251
 
209
 
void debuggnutls(int level, const char* string){
 
252
static void debuggnutls(__attribute__((unused)) int level,
 
253
                        const char* string){
210
254
  fprintf(stderr, "%s", string);
211
255
}
212
256
 
213
 
int initgnutls(encrypted_session *es){
 
257
static int initgnutls(mandos_context *mc, gnutls_session_t *session,
 
258
                      gnutls_dh_params_t *dh_params){
214
259
  const char *err;
215
260
  int ret;
216
261
  
 
262
  if(debug){
 
263
    fprintf(stderr, "Initializing GnuTLS\n");
 
264
  }
 
265
 
217
266
  if ((ret = gnutls_global_init ())
218
267
      != GNUTLS_E_SUCCESS) {
219
268
    fprintf (stderr, "global_init: %s\n", safer_gnutls_strerror(ret));
220
269
    return -1;
221
270
  }
222
 
 
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
 
 
 
271
  
 
272
  if (debug){
 
273
    gnutls_global_set_log_level(11);
 
274
    gnutls_global_set_log_function(debuggnutls);
 
275
  }
 
276
  
228
277
  /* openpgp credentials */
229
 
  if ((ret = gnutls_certificate_allocate_credentials (&es->cred))
 
278
  if ((ret = gnutls_certificate_allocate_credentials (&mc->cred))
230
279
      != GNUTLS_E_SUCCESS) {
231
 
    fprintf (stderr, "memory error: %s\n", safer_gnutls_strerror(ret));
 
280
    fprintf (stderr, "memory error: %s\n",
 
281
             safer_gnutls_strerror(ret));
232
282
    return -1;
233
283
  }
234
 
 
 
284
  
 
285
  if(debug){
 
286
    fprintf(stderr, "Attempting to use OpenPGP certificate %s"
 
287
            " and keyfile %s as GnuTLS credentials\n", pubkeyfile,
 
288
            seckeyfile);
 
289
  }
 
290
  
235
291
  ret = gnutls_certificate_set_openpgp_key_file
236
 
    (es->cred, CERTFILE, KEYFILE, GNUTLS_OPENPGP_FMT_BASE64);
 
292
    (mc->cred, pubkeyfile, seckeyfile, GNUTLS_OPENPGP_FMT_BASE64);
237
293
  if (ret != GNUTLS_E_SUCCESS) {
238
294
    fprintf
239
 
      (stderr, "Error[%d] while reading the OpenPGP key pair ('%s', '%s')\n",
240
 
       ret, CERTFILE, KEYFILE);
 
295
      (stderr, "Error[%d] while reading the OpenPGP key pair ('%s',"
 
296
       " '%s')\n",
 
297
       ret, pubkeyfile, seckeyfile);
241
298
    fprintf(stdout, "The Error is: %s\n",
242
299
            safer_gnutls_strerror(ret));
243
300
    return -1;
244
301
  }
245
 
 
246
 
  //Gnutls server initialization
247
 
  if ((ret = gnutls_dh_params_init (&es->dh_params))
 
302
  
 
303
  //GnuTLS server initialization
 
304
  if ((ret = gnutls_dh_params_init(dh_params))
248
305
      != GNUTLS_E_SUCCESS) {
249
306
    fprintf (stderr, "Error in dh parameter initialization: %s\n",
250
307
             safer_gnutls_strerror(ret));
251
308
    return -1;
252
309
  }
253
 
 
254
 
  if ((ret = gnutls_dh_params_generate2 (es->dh_params, DH_BITS))
 
310
  
 
311
  if ((ret = gnutls_dh_params_generate2(*dh_params, mc->dh_bits))
255
312
      != GNUTLS_E_SUCCESS) {
256
313
    fprintf (stderr, "Error in prime generation: %s\n",
257
314
             safer_gnutls_strerror(ret));
258
315
    return -1;
259
316
  }
260
 
 
261
 
  gnutls_certificate_set_dh_params (es->cred, es->dh_params);
262
 
 
263
 
  // Gnutls session creation
264
 
  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))
265
322
      != GNUTLS_E_SUCCESS){
266
 
    fprintf(stderr, "Error in gnutls session initialization: %s\n",
 
323
    fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
267
324
            safer_gnutls_strerror(ret));
268
325
  }
269
 
 
270
 
  if ((ret = gnutls_priority_set_direct (es->session, "NORMAL", &err))
 
326
  
 
327
  if ((ret = gnutls_priority_set_direct(*session, mc->priority, &err))
271
328
      != GNUTLS_E_SUCCESS) {
272
329
    fprintf(stderr, "Syntax error at: %s\n", err);
273
 
    fprintf(stderr, "Gnutls error: %s\n",
 
330
    fprintf(stderr, "GnuTLS error: %s\n",
274
331
            safer_gnutls_strerror(ret));
275
332
    return -1;
276
333
  }
277
 
 
278
 
  if ((ret = gnutls_credentials_set
279
 
       (es->session, GNUTLS_CRD_CERTIFICATE, es->cred))
 
334
  
 
335
  if ((ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
 
336
                                    mc->cred))
280
337
      != GNUTLS_E_SUCCESS) {
281
338
    fprintf(stderr, "Error setting a credentials set: %s\n",
282
339
            safer_gnutls_strerror(ret));
283
340
    return -1;
284
341
  }
285
 
 
 
342
  
286
343
  /* ignore client certificate if any. */
287
 
  gnutls_certificate_server_set_request (es->session, GNUTLS_CERT_IGNORE);
 
344
  gnutls_certificate_server_set_request (*session,
 
345
                                         GNUTLS_CERT_IGNORE);
288
346
  
289
 
  gnutls_dh_set_prime_bits (es->session, DH_BITS);
 
347
  gnutls_dh_set_prime_bits (*session, mc->dh_bits);
290
348
  
291
349
  return 0;
292
350
}
293
351
 
294
 
void empty_log(AvahiLogLevel level, const char *txt){}
 
352
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
 
353
                      __attribute__((unused)) const char *txt){}
295
354
 
296
 
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){
297
358
  int ret, tcp_sd;
298
359
  struct sockaddr_in6 to;
299
 
  struct in6_addr ip_addr;
300
 
  encrypted_session es;
301
360
  char *buffer = NULL;
302
361
  char *decrypted_buffer;
303
362
  size_t buffer_length = 0;
304
363
  size_t buffer_capacity = 0;
305
364
  ssize_t decrypted_buffer_size;
 
365
  size_t written = 0;
306
366
  int retval = 0;
307
 
 
 
367
  char interface[IF_NAMESIZE];
 
368
  gnutls_session_t session;
 
369
  gnutls_dh_params_t dh_params;
 
370
  
 
371
  if(debug){
 
372
    fprintf(stderr, "Setting up a tcp connection to %s, port %d\n",
 
373
            ip, port);
 
374
  }
308
375
  
309
376
  tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
310
377
  if(tcp_sd < 0) {
311
378
    perror("socket");
312
379
    return -1;
313
380
  }
314
 
  
315
 
  ret = setsockopt(tcp_sd, SOL_SOCKET, SO_BINDTODEVICE, "eth0", 5);
316
 
  if(tcp_sd < 0) {
317
 
    perror("setsockopt bindtodevice");
318
 
    return -1;
 
381
 
 
382
  if(debug){
 
383
    if(if_indextoname((unsigned int)if_index, interface) == NULL){
 
384
      perror("if_indextoname");
 
385
      return -1;
 
386
    }
 
387
    fprintf(stderr, "Binding to interface %s\n", interface);
319
388
  }
320
389
  
321
 
  memset(&to,0,sizeof(to));
 
390
  memset(&to,0,sizeof(to));     /* Spurious warning */
322
391
  to.sin6_family = AF_INET6;
323
 
  ret = inet_pton(AF_INET6, ip, &ip_addr);
 
392
  ret = inet_pton(AF_INET6, ip, &to.sin6_addr);
324
393
  if (ret < 0 ){
325
394
    perror("inet_pton");
326
395
    return -1;
327
 
  }  
 
396
  }
328
397
  if(ret == 0){
329
398
    fprintf(stderr, "Bad address: %s\n", ip);
330
399
    return -1;
331
400
  }
332
 
  to.sin6_port = htons(port);
333
 
  to.sin6_scope_id = if_nametoindex("eth0");
 
401
  to.sin6_port = htons(port);   /* Spurious warning */
 
402
  
 
403
  to.sin6_scope_id = (uint32_t)if_index;
 
404
  
 
405
  if(debug){
 
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
    }
 
416
  }
334
417
  
335
418
  ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
336
419
  if (ret < 0){
338
421
    return -1;
339
422
  }
340
423
  
341
 
  ret = initgnutls (&es);
 
424
  ret = initgnutls (mc, &session, &dh_params);
342
425
  if (ret != 0){
343
426
    retval = -1;
344
427
    return -1;
345
428
  }
346
 
    
347
 
  
348
 
  gnutls_transport_set_ptr (es.session, (gnutls_transport_ptr_t) tcp_sd);
349
 
 
350
 
  ret = gnutls_handshake (es.session);
 
429
  
 
430
  gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t) tcp_sd);
 
431
  
 
432
  if(debug){
 
433
    fprintf(stderr, "Establishing TLS session with %s\n", ip);
 
434
  }
 
435
  
 
436
  ret = gnutls_handshake (session);
351
437
  
352
438
  if (ret != GNUTLS_E_SUCCESS){
353
 
    fprintf(stderr, "\n*** Handshake failed ***\n");
354
 
    gnutls_perror (ret);
 
439
    if(debug){
 
440
      fprintf(stderr, "\n*** Handshake failed ***\n");
 
441
      gnutls_perror (ret);
 
442
    }
355
443
    retval = -1;
356
444
    goto exit;
357
445
  }
 
446
  
 
447
  //Retrieve OpenPGP packet that contains the wanted password
 
448
  
 
449
  if(debug){
 
450
    fprintf(stderr, "Retrieving pgp encrypted password from %s\n",
 
451
            ip);
 
452
  }
358
453
 
359
 
  //retrive password
360
454
  while(true){
361
455
    if (buffer_length + BUFFER_SIZE > buffer_capacity){
362
456
      buffer = realloc(buffer, buffer_capacity + BUFFER_SIZE);
367
461
      buffer_capacity += BUFFER_SIZE;
368
462
    }
369
463
    
370
 
    ret = gnutls_record_recv
371
 
      (es.session, buffer+buffer_length, BUFFER_SIZE);
 
464
    ret = gnutls_record_recv(session, buffer+buffer_length,
 
465
                             BUFFER_SIZE);
372
466
    if (ret == 0){
373
467
      break;
374
468
    }
378
472
      case GNUTLS_E_AGAIN:
379
473
        break;
380
474
      case GNUTLS_E_REHANDSHAKE:
381
 
        ret = gnutls_handshake (es.session);
 
475
        ret = gnutls_handshake (session);
382
476
        if (ret < 0){
383
477
          fprintf(stderr, "\n*** Handshake failed ***\n");
384
478
          gnutls_perror (ret);
387
481
        }
388
482
        break;
389
483
      default:
390
 
        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");
391
486
        retval = -1;
392
 
        gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
 
487
        gnutls_bye (session, GNUTLS_SHUT_RDWR);
393
488
        goto exit;
394
489
      }
395
490
    } else {
396
 
      buffer_length += ret;
 
491
      buffer_length += (size_t) ret;
397
492
    }
398
493
  }
399
 
 
 
494
  
400
495
  if (buffer_length > 0){
401
 
    if ((decrypted_buffer_size = gpg_packet_decrypt(buffer, buffer_length, &decrypted_buffer, CERT_ROOT)) == 0){
 
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
      }
 
515
      free(decrypted_buffer);
 
516
    } else {
402
517
      retval = -1;
403
 
    } else {
404
 
      fwrite (decrypted_buffer, 1, decrypted_buffer_size, stdout);
405
 
      free(decrypted_buffer);
406
518
    }
407
519
  }
408
520
 
 
521
  //shutdown procedure
 
522
 
 
523
  if(debug){
 
524
    fprintf(stderr, "Closing TLS session\n");
 
525
  }
 
526
 
409
527
  free(buffer);
410
 
 
411
 
  //shutdown procedure
412
 
  gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
 
528
  gnutls_bye (session, GNUTLS_SHUT_RDWR);
413
529
 exit:
414
530
  close(tcp_sd);
415
 
  gnutls_deinit (es.session);
416
 
  gnutls_certificate_free_credentials (es.cred);
 
531
  gnutls_deinit (session);
 
532
  gnutls_certificate_free_credentials (mc->cred);
417
533
  gnutls_global_deinit ();
418
534
  return retval;
419
535
}
420
536
 
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
 
}
 
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
 
505
648
 
506
649
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
507
650
    AvahiServerConfig config;
508
651
    AvahiSServiceBrowser *sb = NULL;
509
652
    int error;
510
 
    int ret = 1;
511
 
 
512
 
    avahi_set_log_function(empty_log);
 
653
    int ret;
 
654
    int debug_int;
 
655
    int returncode = EXIT_SUCCESS;
 
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);
 
709
        }
 
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
    
 
789
    if (not debug){
 
790
      avahi_set_log_function(empty_log);
 
791
    }
513
792
    
514
793
    /* Initialize the psuedo-RNG */
515
 
    srand(time(NULL));
 
794
    srand((unsigned int) time(NULL));
516
795
 
517
796
    /* Allocate main loop object */
518
 
    if (!(simple_poll = avahi_simple_poll_new())) {
 
797
    if (!(mc.simple_poll = avahi_simple_poll_new())) {
519
798
        fprintf(stderr, "Failed to create simple poll object.\n");
520
 
        goto fail;
 
799
        returncode = EXIT_FAILURE;
 
800
        goto exit;
521
801
    }
522
802
 
523
803
    /* Do not publish any local records */
527
807
    config.publish_workstation = 0;
528
808
    config.publish_domain = 0;
529
809
 
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
 
    
535
810
    /* Allocate a new server */
536
 
    server = avahi_server_new(avahi_simple_poll_get(simple_poll), &config, NULL, NULL, &error);
537
 
 
 
811
    mc.server=avahi_server_new(avahi_simple_poll_get(mc.simple_poll),
 
812
                               &config, NULL, NULL, &error);
 
813
    
538
814
    /* Free the configuration data */
539
815
    avahi_server_config_free(&config);
540
 
 
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;
 
816
    
 
817
    /* Check if creating the server object succeeded */
 
818
    if (!mc.server) {
 
819
        fprintf(stderr, "Failed to create server: %s\n",
 
820
                avahi_strerror(error));
 
821
        returncode = EXIT_FAILURE;
 
822
        goto exit;
545
823
    }
546
824
    
547
825
    /* Create the service browser */
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;
 
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)));
 
833
        returncode = EXIT_FAILURE;
 
834
        goto exit;
551
835
    }
552
836
    
553
837
    /* Run the main loop */
554
 
    avahi_simple_poll_loop(simple_poll);
555
 
    
556
 
    ret = 0;
557
 
    
558
 
fail:
 
838
 
 
839
    if (debug){
 
840
      fprintf(stderr, "Starting avahi loop search\n");
 
841
    }
 
842
    
 
843
    avahi_simple_poll_loop(mc.simple_poll);
 
844
    
 
845
 exit:
 
846
 
 
847
    if (debug){
 
848
      fprintf(stderr, "%s exiting\n", argv[0]);
 
849
    }
559
850
    
560
851
    /* Cleanup things */
561
852
    if (sb)
562
853
        avahi_s_service_browser_free(sb);
563
854
    
564
 
    if (server)
565
 
        avahi_server_free(server);
566
 
 
567
 
    if (simple_poll)
568
 
        avahi_simple_poll_free(simple_poll);
569
 
 
570
 
    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;
571
864
}