/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

plugbasedclient
        change uid to nobody:nogroup as default for all plugins

mandosclient
        Takes up the interface if needed

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/*  -*- coding: utf-8 -*- */
2
2
/*
3
 
 * Mandos-client - get and decrypt data from a Mandos server
 
3
 * Mandos client - get and decrypt data from a Mandos server
4
4
 *
5
5
 * This program is partly derived from an example program for an Avahi
6
6
 * service browser, downloaded from
8
8
 * includes the following functions: "resolve_callback",
9
9
 * "browse_callback", and parts of "main".
10
10
 * 
11
 
 * Everything else is
12
 
 * Copyright © 2008,2009 Teddy Hogeborn
13
 
 * Copyright © 2008,2009 Björn Påhlsson
 
11
 * Everything else is Copyright © 2007-2008 Teddy Hogeborn and Björn
 
12
 * Påhlsson.
14
13
 * 
15
14
 * This program is free software: you can redistribute it and/or
16
15
 * modify it under the terms of the GNU General Public License as
26
25
 * along with this program.  If not, see
27
26
 * <http://www.gnu.org/licenses/>.
28
27
 * 
29
 
 * Contact the authors at <mandos@fukt.bsnet.se>.
 
28
 * Contact the authors at <https://www.fukt.bsnet.se/~belorn/> and
 
29
 * <https://www.fukt.bsnet.se/~teddy/>.
30
30
 */
31
31
 
32
 
/* Needed by GPGME, specifically gpgme_data_seek() */
 
32
#define _FORTIFY_SOURCE 2
 
33
 
33
34
#define _LARGEFILE_SOURCE
34
35
#define _FILE_OFFSET_BITS 64
35
36
 
36
 
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY(), asprintf() */
37
 
 
38
 
#include <stdio.h>              /* fprintf(), stderr, fwrite(),
39
 
                                   stdout, ferror(), sscanf */
40
 
#include <stdint.h>             /* uint16_t, uint32_t */
41
 
#include <stddef.h>             /* NULL, size_t, ssize_t */
42
 
#include <stdlib.h>             /* free(), EXIT_SUCCESS, EXIT_FAILURE,
43
 
                                   srand() */
44
 
#include <stdbool.h>            /* bool, true */
45
 
#include <string.h>             /* memset(), strcmp(), strlen(),
46
 
                                   strerror(), asprintf(), strcpy() */
47
 
#include <sys/ioctl.h>          /* ioctl */
48
 
#include <sys/types.h>          /* socket(), inet_pton(), sockaddr,
49
 
                                   sockaddr_in6, PF_INET6,
50
 
                                   SOCK_STREAM, INET6_ADDRSTRLEN,
51
 
                                   uid_t, gid_t, open(), opendir(),
52
 
                                   DIR */
53
 
#include <sys/stat.h>           /* open() */
54
 
#include <sys/socket.h>         /* socket(), struct sockaddr_in6,
55
 
                                   struct in6_addr, inet_pton(),
56
 
                                   connect() */
57
 
#include <fcntl.h>              /* open() */
58
 
#include <dirent.h>             /* opendir(), struct dirent, readdir()
59
 
                                 */
60
 
#include <inttypes.h>           /* PRIu16, intmax_t, SCNdMAX */
61
 
#include <assert.h>             /* assert() */
62
 
#include <errno.h>              /* perror(), errno */
63
 
#include <time.h>               /* time() */
64
 
#include <net/if.h>             /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
65
 
                                   SIOCSIFFLAGS, if_indextoname(),
66
 
                                   if_nametoindex(), IF_NAMESIZE */
67
 
#include <netinet/in.h>
68
 
#include <unistd.h>             /* close(), SEEK_SET, off_t, write(),
69
 
                                   getuid(), getgid(), setuid(),
70
 
                                   setgid() */
71
 
#include <arpa/inet.h>          /* inet_pton(), htons */
72
 
#include <iso646.h>             /* not, and, or */
73
 
#include <argp.h>               /* struct argp_option, error_t, struct
74
 
                                   argp_state, struct argp,
75
 
                                   argp_parse(), ARGP_KEY_ARG,
76
 
                                   ARGP_KEY_END, ARGP_ERR_UNKNOWN */
77
 
 
78
 
/* Avahi */
79
 
/* All Avahi types, constants and functions
80
 
 Avahi*, avahi_*,
81
 
 AVAHI_* */
 
37
#include <stdio.h>
 
38
#include <assert.h>
 
39
#include <stdlib.h>
 
40
#include <time.h>
 
41
#include <net/if.h>             /* if_nametoindex */
 
42
#include <sys/ioctl.h>          // ioctl, ifreq, SIOCGIFFLAGS, IFF_UP, SIOCSIFFLAGS
 
43
#include <net/if.h>             // ioctl, ifreq, SIOCGIFFLAGS, IFF_UP, SIOCSIFFLAGS
 
44
 
82
45
#include <avahi-core/core.h>
83
46
#include <avahi-core/lookup.h>
84
47
#include <avahi-core/log.h>
86
49
#include <avahi-common/malloc.h>
87
50
#include <avahi-common/error.h>
88
51
 
89
 
/* GnuTLS */
90
 
#include <gnutls/gnutls.h>      /* All GnuTLS types, constants and
91
 
                                   functions:
92
 
                                   gnutls_*
93
 
                                   init_gnutls_session(),
94
 
                                   GNUTLS_* */
95
 
#include <gnutls/openpgp.h>
96
 
                          /* gnutls_certificate_set_openpgp_key_file(),
97
 
                                   GNUTLS_OPENPGP_FMT_BASE64 */
98
 
 
99
 
/* GPGME */
100
 
#include <gpgme.h>              /* All GPGME types, constants and
101
 
                                   functions:
102
 
                                   gpgme_*
103
 
                                   GPGME_PROTOCOL_OpenPGP,
104
 
                                   GPG_ERR_NO_* */
 
52
//mandos client part
 
53
#include <sys/types.h>          /* socket(), inet_pton() */
 
54
#include <sys/socket.h>         /* socket(), struct sockaddr_in6,
 
55
                                   struct in6_addr, inet_pton() */
 
56
#include <gnutls/gnutls.h>      /* All GnuTLS stuff */
 
57
#include <gnutls/openpgp.h>     /* GnuTLS with openpgp stuff */
 
58
 
 
59
#include <unistd.h>             /* close() */
 
60
#include <netinet/in.h>
 
61
#include <stdbool.h>            /* true */
 
62
#include <string.h>             /* memset */
 
63
#include <arpa/inet.h>          /* inet_pton() */
 
64
#include <iso646.h>             /* not */
 
65
 
 
66
// gpgme
 
67
#include <errno.h>              /* perror() */
 
68
#include <gpgme.h>
 
69
 
 
70
// getopt long
 
71
#include <getopt.h>
105
72
 
106
73
#define BUFFER_SIZE 256
 
74
#define DH_BITS 1024
107
75
 
108
 
#define PATHDIR "/conf/conf.d/mandos"
109
 
#define SECKEY "seckey.txt"
110
 
#define PUBKEY "pubkey.txt"
 
76
const char *certdir = "/conf/conf.d/cryptkeyreq/";
 
77
const char *certfile = "openpgp-client.txt";
 
78
const char *certkey = "openpgp-client-key.txt";
111
79
 
112
80
bool debug = false;
113
 
static const char mandos_protocol_version[] = "1";
114
 
const char *argp_program_version = "mandos-client " VERSION;
115
 
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
116
81
 
117
 
/* Used for passing in values through the Avahi callback functions */
118
82
typedef struct {
119
 
  AvahiSimplePoll *simple_poll;
120
 
  AvahiServer *server;
 
83
  gnutls_session_t session;
121
84
  gnutls_certificate_credentials_t cred;
122
 
  unsigned int dh_bits;
123
85
  gnutls_dh_params_t dh_params;
124
 
  const char *priority;
 
86
} encrypted_session;
 
87
 
 
88
 
 
89
ssize_t pgp_packet_decrypt (char *packet, size_t packet_size,
 
90
                            char **new_packet, const char *homedir){
 
91
  gpgme_data_t dh_crypto, dh_plain;
125
92
  gpgme_ctx_t ctx;
126
 
} mandos_context;
127
 
 
128
 
/*
129
 
 * Make room in "buffer" for at least BUFFER_SIZE additional bytes.
130
 
 * "buffer_capacity" is how much is currently allocated,
131
 
 * "buffer_length" is how much is already used.
132
 
 */
133
 
size_t adjustbuffer(char **buffer, size_t buffer_length,
134
 
                  size_t buffer_capacity){
135
 
  if(buffer_length + BUFFER_SIZE > buffer_capacity){
136
 
    *buffer = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
137
 
    if(buffer == NULL){
138
 
      return 0;
139
 
    }
140
 
    buffer_capacity += BUFFER_SIZE;
141
 
  }
142
 
  return buffer_capacity;
143
 
}
144
 
 
145
 
/* 
146
 
 * Initialize GPGME.
147
 
 */
148
 
static bool init_gpgme(mandos_context *mc, const char *seckey,
149
 
                       const char *pubkey, const char *tempdir){
150
 
  int ret;
151
93
  gpgme_error_t rc;
 
94
  ssize_t ret;
 
95
  ssize_t new_packet_capacity = 0;
 
96
  ssize_t new_packet_length = 0;
152
97
  gpgme_engine_info_t engine_info;
153
 
  
154
 
  
155
 
  /*
156
 
   * Helper function to insert pub and seckey to the enigne keyring.
157
 
   */
158
 
  bool import_key(const char *filename){
159
 
    int fd;
160
 
    gpgme_data_t pgp_data;
161
 
    
162
 
    fd = (int)TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
163
 
    if(fd == -1){
164
 
      perror("open");
165
 
      return false;
166
 
    }
167
 
    
168
 
    rc = gpgme_data_new_from_fd(&pgp_data, fd);
169
 
    if(rc != GPG_ERR_NO_ERROR){
170
 
      fprintf(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
171
 
              gpgme_strsource(rc), gpgme_strerror(rc));
172
 
      return false;
173
 
    }
174
 
    
175
 
    rc = gpgme_op_import(mc->ctx, pgp_data);
176
 
    if(rc != GPG_ERR_NO_ERROR){
177
 
      fprintf(stderr, "bad gpgme_op_import: %s: %s\n",
178
 
              gpgme_strsource(rc), gpgme_strerror(rc));
179
 
      return false;
180
 
    }
181
 
    
182
 
    ret = (int)TEMP_FAILURE_RETRY(close(fd));
183
 
    if(ret == -1){
184
 
      perror("close");
185
 
    }
186
 
    gpgme_data_release(pgp_data);
187
 
    return true;
188
 
  }
189
 
  
190
 
  if(debug){
191
 
    fprintf(stderr, "Initialize gpgme\n");
 
98
 
 
99
  if (debug){
 
100
    fprintf(stderr, "Trying to decrypt OpenPGP packet\n");
192
101
  }
193
102
  
194
103
  /* Init GPGME */
195
104
  gpgme_check_version(NULL);
196
105
  rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
197
 
  if(rc != GPG_ERR_NO_ERROR){
 
106
  if (rc != GPG_ERR_NO_ERROR){
198
107
    fprintf(stderr, "bad gpgme_engine_check_version: %s: %s\n",
199
108
            gpgme_strsource(rc), gpgme_strerror(rc));
200
 
    return false;
 
109
    return -1;
201
110
  }
202
111
  
203
 
    /* Set GPGME home directory for the OpenPGP engine only */
204
 
  rc = gpgme_get_engine_info(&engine_info);
205
 
  if(rc != GPG_ERR_NO_ERROR){
 
112
  /* Set GPGME home directory */
 
113
  rc = gpgme_get_engine_info (&engine_info);
 
114
  if (rc != GPG_ERR_NO_ERROR){
206
115
    fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
207
116
            gpgme_strsource(rc), gpgme_strerror(rc));
208
 
    return false;
 
117
    return -1;
209
118
  }
210
119
  while(engine_info != NULL){
211
120
    if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
212
121
      gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
213
 
                            engine_info->file_name, tempdir);
 
122
                            engine_info->file_name, homedir);
214
123
      break;
215
124
    }
216
125
    engine_info = engine_info->next;
217
126
  }
218
127
  if(engine_info == NULL){
219
 
    fprintf(stderr, "Could not set GPGME home dir to %s\n", tempdir);
220
 
    return false;
221
 
  }
222
 
  
223
 
  /* Create new GPGME "context" */
224
 
  rc = gpgme_new(&(mc->ctx));
225
 
  if(rc != GPG_ERR_NO_ERROR){
226
 
    fprintf(stderr, "bad gpgme_new: %s: %s\n",
227
 
            gpgme_strsource(rc), gpgme_strerror(rc));
228
 
    return false;
229
 
  }
230
 
  
231
 
  if(not import_key(pubkey) or not import_key(seckey)){
232
 
    return false;
233
 
  }
234
 
  
235
 
  return true; 
236
 
}
237
 
 
238
 
/* 
239
 
 * Decrypt OpenPGP data.
240
 
 * Returns -1 on error
241
 
 */
242
 
static ssize_t pgp_packet_decrypt(const mandos_context *mc,
243
 
                                  const char *cryptotext,
244
 
                                  size_t crypto_size,
245
 
                                  char **plaintext){
246
 
  gpgme_data_t dh_crypto, dh_plain;
247
 
  gpgme_error_t rc;
248
 
  ssize_t ret;
249
 
  size_t plaintext_capacity = 0;
250
 
  ssize_t plaintext_length = 0;
251
 
  
252
 
  if(debug){
253
 
    fprintf(stderr, "Trying to decrypt OpenPGP data\n");
254
 
  }
255
 
  
256
 
  /* Create new GPGME data buffer from memory cryptotext */
257
 
  rc = gpgme_data_new_from_mem(&dh_crypto, cryptotext, crypto_size,
258
 
                               0);
259
 
  if(rc != GPG_ERR_NO_ERROR){
 
128
    fprintf(stderr, "Could not set home dir to %s\n", homedir);
 
129
    return -1;
 
130
  }
 
131
  
 
132
  /* Create new GPGME data buffer from packet buffer */
 
133
  rc = gpgme_data_new_from_mem(&dh_crypto, packet, packet_size, 0);
 
134
  if (rc != GPG_ERR_NO_ERROR){
260
135
    fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
261
136
            gpgme_strsource(rc), gpgme_strerror(rc));
262
137
    return -1;
264
139
  
265
140
  /* Create new empty GPGME data buffer for the plaintext */
266
141
  rc = gpgme_data_new(&dh_plain);
267
 
  if(rc != GPG_ERR_NO_ERROR){
 
142
  if (rc != GPG_ERR_NO_ERROR){
268
143
    fprintf(stderr, "bad gpgme_data_new: %s: %s\n",
269
144
            gpgme_strsource(rc), gpgme_strerror(rc));
270
 
    gpgme_data_release(dh_crypto);
271
 
    return -1;
272
 
  }
273
 
  
274
 
  /* Decrypt data from the cryptotext data buffer to the plaintext
275
 
     data buffer */
276
 
  rc = gpgme_op_decrypt(mc->ctx, dh_crypto, dh_plain);
277
 
  if(rc != GPG_ERR_NO_ERROR){
 
145
    return -1;
 
146
  }
 
147
  
 
148
  /* Create new GPGME "context" */
 
149
  rc = gpgme_new(&ctx);
 
150
  if (rc != GPG_ERR_NO_ERROR){
 
151
    fprintf(stderr, "bad gpgme_new: %s: %s\n",
 
152
            gpgme_strsource(rc), gpgme_strerror(rc));
 
153
    return -1;
 
154
  }
 
155
  
 
156
  /* Decrypt data from the FILE pointer to the plaintext data
 
157
     buffer */
 
158
  rc = gpgme_op_decrypt(ctx, dh_crypto, dh_plain);
 
159
  if (rc != GPG_ERR_NO_ERROR){
278
160
    fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
279
161
            gpgme_strsource(rc), gpgme_strerror(rc));
280
 
    plaintext_length = -1;
281
 
    if(debug){
282
 
      gpgme_decrypt_result_t result;
283
 
      result = gpgme_op_decrypt_result(mc->ctx);
284
 
      if(result == NULL){
285
 
        fprintf(stderr, "gpgme_op_decrypt_result failed\n");
286
 
      } else {
287
 
        fprintf(stderr, "Unsupported algorithm: %s\n",
288
 
                result->unsupported_algorithm);
289
 
        fprintf(stderr, "Wrong key usage: %u\n",
290
 
                result->wrong_key_usage);
291
 
        if(result->file_name != NULL){
292
 
          fprintf(stderr, "File name: %s\n", result->file_name);
293
 
        }
294
 
        gpgme_recipient_t recipient;
295
 
        recipient = result->recipients;
296
 
        if(recipient){
297
 
          while(recipient != NULL){
298
 
            fprintf(stderr, "Public key algorithm: %s\n",
299
 
                    gpgme_pubkey_algo_name(recipient->pubkey_algo));
300
 
            fprintf(stderr, "Key ID: %s\n", recipient->keyid);
301
 
            fprintf(stderr, "Secret key available: %s\n",
302
 
                    recipient->status == GPG_ERR_NO_SECKEY
303
 
                    ? "No" : "Yes");
304
 
            recipient = recipient->next;
305
 
          }
 
162
    return -1;
 
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;
306
193
        }
307
194
      }
308
195
    }
309
 
    goto decrypt_end;
310
196
  }
311
197
  
312
 
  if(debug){
313
 
    fprintf(stderr, "Decryption of OpenPGP data succeeded\n");
314
 
  }
 
198
  /* Delete the GPGME FILE pointer cryptotext data buffer */
 
199
  gpgme_data_release(dh_crypto);
315
200
  
316
201
  /* Seek back to the beginning of the GPGME plaintext data buffer */
317
 
  if(gpgme_data_seek(dh_plain, (off_t)0, SEEK_SET) == -1){
318
 
    perror("gpgme_data_seek");
319
 
    plaintext_length = -1;
320
 
    goto decrypt_end;
 
202
  if (gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET) == -1){
 
203
    perror("pgpme_data_seek");
321
204
  }
322
205
  
323
 
  *plaintext = NULL;
 
206
  *new_packet = 0;
324
207
  while(true){
325
 
    plaintext_capacity = adjustbuffer(plaintext,
326
 
                                      (size_t)plaintext_length,
327
 
                                      plaintext_capacity);
328
 
    if(plaintext_capacity == 0){
329
 
        perror("adjustbuffer");
330
 
        plaintext_length = -1;
331
 
        goto decrypt_end;
 
208
    if (new_packet_length + BUFFER_SIZE > new_packet_capacity){
 
209
      *new_packet = realloc(*new_packet,
 
210
                            (unsigned int)new_packet_capacity
 
211
                            + BUFFER_SIZE);
 
212
      if (*new_packet == NULL){
 
213
        perror("realloc");
 
214
        return -1;
 
215
      }
 
216
      new_packet_capacity += BUFFER_SIZE;
332
217
    }
333
218
    
334
 
    ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
 
219
    ret = gpgme_data_read(dh_plain, *new_packet + new_packet_length,
335
220
                          BUFFER_SIZE);
336
221
    /* Print the data, if any */
337
 
    if(ret == 0){
338
 
      /* EOF */
 
222
    if (ret == 0){
339
223
      break;
340
224
    }
341
225
    if(ret < 0){
342
226
      perror("gpgme_data_read");
343
 
      plaintext_length = -1;
344
 
      goto decrypt_end;
345
 
    }
346
 
    plaintext_length += ret;
347
 
  }
348
 
  
349
 
  if(debug){
350
 
    fprintf(stderr, "Decrypted password is: ");
351
 
    for(ssize_t i = 0; i < plaintext_length; i++){
352
 
      fprintf(stderr, "%02hhX ", (*plaintext)[i]);
353
 
    }
354
 
    fprintf(stderr, "\n");
355
 
  }
356
 
  
357
 
 decrypt_end:
358
 
  
359
 
  /* Delete the GPGME cryptotext data buffer */
360
 
  gpgme_data_release(dh_crypto);
 
227
      return -1;
 
228
    }
 
229
    new_packet_length += ret;
 
230
  }
 
231
 
 
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
  /*   } */
361
239
  
362
240
  /* Delete the GPGME plaintext data buffer */
363
241
  gpgme_data_release(dh_plain);
364
 
  return plaintext_length;
 
242
  return new_packet_length;
365
243
}
366
244
 
367
 
static const char * safer_gnutls_strerror(int value) {
368
 
  const char *ret = gnutls_strerror(value); /* Spurious warning from
369
 
                                               -Wunreachable-code */
370
 
  if(ret == NULL)
 
245
static const char * safer_gnutls_strerror (int value) {
 
246
  const char *ret = gnutls_strerror (value);
 
247
  if (ret == NULL)
371
248
    ret = "(unknown)";
372
249
  return ret;
373
250
}
374
251
 
375
 
/* GnuTLS log function callback */
376
 
static void debuggnutls(__attribute__((unused)) int level,
377
 
                        const char* string){
378
 
  fprintf(stderr, "GnuTLS: %s", string);
 
252
void debuggnutls(__attribute__((unused)) int level,
 
253
                 const char* string){
 
254
  fprintf(stderr, "%s", string);
379
255
}
380
256
 
381
 
static int init_gnutls_global(mandos_context *mc,
382
 
                              const char *pubkeyfilename,
383
 
                              const char *seckeyfilename){
 
257
int initgnutls(encrypted_session *es){
 
258
  const char *err;
384
259
  int ret;
385
260
  
386
261
  if(debug){
387
262
    fprintf(stderr, "Initializing GnuTLS\n");
388
263
  }
389
 
  
390
 
  ret = gnutls_global_init();
391
 
  if(ret != GNUTLS_E_SUCCESS) {
392
 
    fprintf(stderr, "GnuTLS global_init: %s\n",
393
 
            safer_gnutls_strerror(ret));
 
264
 
 
265
  if ((ret = gnutls_global_init ())
 
266
      != GNUTLS_E_SUCCESS) {
 
267
    fprintf (stderr, "global_init: %s\n", safer_gnutls_strerror(ret));
394
268
    return -1;
395
269
  }
396
 
  
397
 
  if(debug){
398
 
    /* "Use a log level over 10 to enable all debugging options."
399
 
     * - GnuTLS manual
400
 
     */
 
270
 
 
271
  if (debug){
401
272
    gnutls_global_set_log_level(11);
402
273
    gnutls_global_set_log_function(debuggnutls);
403
274
  }
404
275
  
405
 
  /* OpenPGP credentials */
406
 
  gnutls_certificate_allocate_credentials(&mc->cred);
407
 
  if(ret != GNUTLS_E_SUCCESS){
408
 
    fprintf(stderr, "GnuTLS memory error: %s\n", /* Spurious warning
409
 
                                                  * from
410
 
                                                  * -Wunreachable-code
411
 
                                                  */
412
 
            safer_gnutls_strerror(ret));
413
 
    gnutls_global_deinit();
 
276
  /* openpgp credentials */
 
277
  if ((ret = gnutls_certificate_allocate_credentials (&es->cred))
 
278
      != GNUTLS_E_SUCCESS) {
 
279
    fprintf (stderr, "memory error: %s\n",
 
280
             safer_gnutls_strerror(ret));
414
281
    return -1;
415
282
  }
416
283
  
417
284
  if(debug){
418
 
    fprintf(stderr, "Attempting to use OpenPGP public key %s and"
419
 
            " secret key %s as GnuTLS credentials\n", pubkeyfilename,
420
 
            seckeyfilename);
 
285
    fprintf(stderr, "Attempting to use OpenPGP certificate %s"
 
286
            " and keyfile %s as GnuTLS credentials\n", certfile,
 
287
            certkey);
421
288
  }
422
289
  
423
290
  ret = gnutls_certificate_set_openpgp_key_file
424
 
    (mc->cred, pubkeyfilename, seckeyfilename,
425
 
     GNUTLS_OPENPGP_FMT_BASE64);
426
 
  if(ret != GNUTLS_E_SUCCESS) {
427
 
    fprintf(stderr,
428
 
            "Error[%d] while reading the OpenPGP key pair ('%s',"
429
 
            " '%s')\n", ret, pubkeyfilename, seckeyfilename);
430
 
    fprintf(stderr, "The GnuTLS error is: %s\n",
431
 
            safer_gnutls_strerror(ret));
432
 
    goto globalfail;
433
 
  }
434
 
  
435
 
  /* GnuTLS server initialization */
436
 
  ret = gnutls_dh_params_init(&mc->dh_params);
437
 
  if(ret != GNUTLS_E_SUCCESS) {
438
 
    fprintf(stderr, "Error in GnuTLS DH parameter initialization:"
439
 
            " %s\n", safer_gnutls_strerror(ret));
440
 
    goto globalfail;
441
 
  }
442
 
  ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
443
 
  if(ret != GNUTLS_E_SUCCESS) {
444
 
    fprintf(stderr, "Error in GnuTLS prime generation: %s\n",
445
 
            safer_gnutls_strerror(ret));
446
 
    goto globalfail;
447
 
  }
448
 
  
449
 
  gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
450
 
  
451
 
  return 0;
452
 
  
453
 
 globalfail:
454
 
  
455
 
  gnutls_certificate_free_credentials(mc->cred);
456
 
  gnutls_global_deinit();
457
 
  gnutls_dh_params_deinit(mc->dh_params);
458
 
  return -1;
459
 
}
460
 
 
461
 
static int init_gnutls_session(mandos_context *mc,
462
 
                               gnutls_session_t *session){
463
 
  int ret;
464
 
  /* GnuTLS session creation */
465
 
  ret = gnutls_init(session, GNUTLS_SERVER);
466
 
  if(ret != GNUTLS_E_SUCCESS){
 
291
    (es->cred, certfile, certkey, GNUTLS_OPENPGP_FMT_BASE64);
 
292
  if (ret != GNUTLS_E_SUCCESS) {
 
293
    fprintf
 
294
      (stderr, "Error[%d] while reading the OpenPGP key pair ('%s',"
 
295
       " '%s')\n",
 
296
       ret, certfile, certkey);
 
297
    fprintf(stdout, "The Error is: %s\n",
 
298
            safer_gnutls_strerror(ret));
 
299
    return -1;
 
300
  }
 
301
  
 
302
  //GnuTLS server initialization
 
303
  if ((ret = gnutls_dh_params_init (&es->dh_params))
 
304
      != GNUTLS_E_SUCCESS) {
 
305
    fprintf (stderr, "Error in dh parameter initialization: %s\n",
 
306
             safer_gnutls_strerror(ret));
 
307
    return -1;
 
308
  }
 
309
  
 
310
  if ((ret = gnutls_dh_params_generate2 (es->dh_params, DH_BITS))
 
311
      != GNUTLS_E_SUCCESS) {
 
312
    fprintf (stderr, "Error in prime generation: %s\n",
 
313
             safer_gnutls_strerror(ret));
 
314
    return -1;
 
315
  }
 
316
  
 
317
  gnutls_certificate_set_dh_params (es->cred, es->dh_params);
 
318
  
 
319
  // GnuTLS session creation
 
320
  if ((ret = gnutls_init (&es->session, GNUTLS_SERVER))
 
321
      != GNUTLS_E_SUCCESS){
467
322
    fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
468
323
            safer_gnutls_strerror(ret));
469
324
  }
470
325
  
471
 
  {
472
 
    const char *err;
473
 
    ret = gnutls_priority_set_direct(*session, mc->priority, &err);
474
 
    if(ret != GNUTLS_E_SUCCESS) {
475
 
      fprintf(stderr, "Syntax error at: %s\n", err);
476
 
      fprintf(stderr, "GnuTLS error: %s\n",
477
 
              safer_gnutls_strerror(ret));
478
 
      gnutls_deinit(*session);
479
 
      return -1;
480
 
    }
 
326
  if ((ret = gnutls_priority_set_direct (es->session, "NORMAL", &err))
 
327
      != GNUTLS_E_SUCCESS) {
 
328
    fprintf(stderr, "Syntax error at: %s\n", err);
 
329
    fprintf(stderr, "GnuTLS error: %s\n",
 
330
            safer_gnutls_strerror(ret));
 
331
    return -1;
481
332
  }
482
333
  
483
 
  ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
484
 
                               mc->cred);
485
 
  if(ret != GNUTLS_E_SUCCESS) {
486
 
    fprintf(stderr, "Error setting GnuTLS credentials: %s\n",
 
334
  if ((ret = gnutls_credentials_set
 
335
       (es->session, GNUTLS_CRD_CERTIFICATE, es->cred))
 
336
      != GNUTLS_E_SUCCESS) {
 
337
    fprintf(stderr, "Error setting a credentials set: %s\n",
487
338
            safer_gnutls_strerror(ret));
488
 
    gnutls_deinit(*session);
489
339
    return -1;
490
340
  }
491
341
  
492
342
  /* ignore client certificate if any. */
493
 
  gnutls_certificate_server_set_request(*session,
494
 
                                        GNUTLS_CERT_IGNORE);
 
343
  gnutls_certificate_server_set_request (es->session,
 
344
                                         GNUTLS_CERT_IGNORE);
495
345
  
496
 
  gnutls_dh_set_prime_bits(*session, mc->dh_bits);
 
346
  gnutls_dh_set_prime_bits (es->session, DH_BITS);
497
347
  
498
348
  return 0;
499
349
}
500
350
 
501
 
/* Avahi log function callback */
502
 
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
503
 
                      __attribute__((unused)) const char *txt){}
 
351
void empty_log(__attribute__((unused)) AvahiLogLevel level,
 
352
               __attribute__((unused)) const char *txt){}
504
353
 
505
 
/* Called when a Mandos server is found */
506
 
static int start_mandos_communication(const char *ip, uint16_t port,
507
 
                                      AvahiIfIndex if_index,
508
 
                                      mandos_context *mc){
 
354
int start_mandos_communication(const char *ip, uint16_t port,
 
355
                               unsigned int if_index){
509
356
  int ret, tcp_sd;
510
 
  ssize_t sret;
511
 
  union { struct sockaddr in; struct sockaddr_in6 in6; } to;
 
357
  struct sockaddr_in6 to;
 
358
  encrypted_session es;
512
359
  char *buffer = NULL;
513
360
  char *decrypted_buffer;
514
361
  size_t buffer_length = 0;
515
362
  size_t buffer_capacity = 0;
516
363
  ssize_t decrypted_buffer_size;
517
 
  size_t written;
 
364
  size_t written = 0;
518
365
  int retval = 0;
519
366
  char interface[IF_NAMESIZE];
520
 
  gnutls_session_t session;
521
 
  
522
 
  ret = init_gnutls_session(mc, &session);
523
 
  if(ret != 0){
524
 
    return -1;
525
 
  }
526
367
  
527
368
  if(debug){
528
 
    fprintf(stderr, "Setting up a tcp connection to %s, port %" PRIu16
529
 
            "\n", ip, port);
 
369
    fprintf(stderr, "Setting up a tcp connection to %s\n", ip);
530
370
  }
531
371
  
532
372
  tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
534
374
    perror("socket");
535
375
    return -1;
536
376
  }
537
 
  
 
377
 
538
378
  if(debug){
539
 
    if(if_indextoname((unsigned int)if_index, interface) == NULL){
540
 
      perror("if_indextoname");
 
379
    if(if_indextoname(if_index, interface) == NULL){
 
380
      if(debug){
 
381
        perror("if_indextoname");
 
382
      }
541
383
      return -1;
542
384
    }
 
385
    
543
386
    fprintf(stderr, "Binding to interface %s\n", interface);
544
387
  }
545
388
  
546
 
  memset(&to, 0, sizeof(to));
547
 
  to.in6.sin6_family = AF_INET6;
548
 
  /* It would be nice to have a way to detect if we were passed an
549
 
     IPv4 address here.   Now we assume an IPv6 address. */
550
 
  ret = inet_pton(AF_INET6, ip, &to.in6.sin6_addr);
551
 
  if(ret < 0 ){
 
389
  memset(&to,0,sizeof(to));     /* Spurious warning */
 
390
  to.sin6_family = AF_INET6;
 
391
  ret = inet_pton(AF_INET6, ip, &to.sin6_addr);
 
392
  if (ret < 0 ){
552
393
    perror("inet_pton");
553
394
    return -1;
554
 
  }
 
395
  }  
555
396
  if(ret == 0){
556
397
    fprintf(stderr, "Bad address: %s\n", ip);
557
398
    return -1;
558
399
  }
559
 
  to.in6.sin6_port = htons(port); /* Spurious warnings from
560
 
                                     -Wconversion and
561
 
                                     -Wunreachable-code */
 
400
  to.sin6_port = htons(port);   /* Spurious warning */
562
401
  
563
 
  to.in6.sin6_scope_id = (uint32_t)if_index;
 
402
  to.sin6_scope_id = (uint32_t)if_index;
564
403
  
565
404
  if(debug){
566
 
    fprintf(stderr, "Connection to: %s, port %" PRIu16 "\n", ip,
567
 
            port);
568
 
    char addrstr[INET6_ADDRSTRLEN] = "";
569
 
    if(inet_ntop(to.in6.sin6_family, &(to.in6.sin6_addr), addrstr,
570
 
                 sizeof(addrstr)) == NULL){
571
 
      perror("inet_ntop");
572
 
    } else {
573
 
      if(strcmp(addrstr, ip) != 0){
574
 
        fprintf(stderr, "Canonical address form: %s\n", addrstr);
575
 
      }
576
 
    }
 
405
    fprintf(stderr, "Connection to: %s\n", ip);
577
406
  }
578
407
  
579
 
  ret = connect(tcp_sd, &to.in, sizeof(to));
580
 
  if(ret < 0){
 
408
  ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
 
409
  if (ret < 0){
581
410
    perror("connect");
582
411
    return -1;
583
412
  }
584
413
  
585
 
  const char *out = mandos_protocol_version;
586
 
  written = 0;
587
 
  while(true){
588
 
    size_t out_size = strlen(out);
589
 
    ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
590
 
                                   out_size - written));
591
 
    if(ret == -1){
592
 
      perror("write");
593
 
      retval = -1;
594
 
      goto mandos_end;
595
 
    }
596
 
    written += (size_t)ret;
597
 
    if(written < out_size){
598
 
      continue;
599
 
    } else {
600
 
      if(out == mandos_protocol_version){
601
 
        written = 0;
602
 
        out = "\r\n";
603
 
      } else {
604
 
        break;
605
 
      }
606
 
    }
 
414
  ret = initgnutls (&es);
 
415
  if (ret != 0){
 
416
    retval = -1;
 
417
    return -1;
607
418
  }
608
419
  
 
420
  gnutls_transport_set_ptr (es.session,
 
421
                            (gnutls_transport_ptr_t) tcp_sd);
 
422
  
609
423
  if(debug){
610
424
    fprintf(stderr, "Establishing TLS session with %s\n", ip);
611
425
  }
612
426
  
613
 
  gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t) tcp_sd);
614
 
  
615
 
  do{
616
 
    ret = gnutls_handshake(session);
617
 
  } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
618
 
  
619
 
  if(ret != GNUTLS_E_SUCCESS){
 
427
  ret = gnutls_handshake (es.session);
 
428
  
 
429
  if (ret != GNUTLS_E_SUCCESS){
620
430
    if(debug){
621
 
      fprintf(stderr, "*** GnuTLS Handshake failed ***\n");
622
 
      gnutls_perror(ret);
 
431
      fprintf(stderr, "\n*** Handshake failed ***\n");
 
432
      gnutls_perror (ret);
623
433
    }
624
434
    retval = -1;
625
 
    goto mandos_end;
 
435
    goto exit;
626
436
  }
627
437
  
628
 
  /* Read OpenPGP packet that contains the wanted password */
 
438
  //Retrieve OpenPGP packet that contains the wanted password
629
439
  
630
440
  if(debug){
631
441
    fprintf(stderr, "Retrieving pgp encrypted password from %s\n",
632
442
            ip);
633
443
  }
634
 
  
 
444
 
635
445
  while(true){
636
 
    buffer_capacity = adjustbuffer(&buffer, buffer_length,
637
 
                                   buffer_capacity);
638
 
    if(buffer_capacity == 0){
639
 
      perror("adjustbuffer");
640
 
      retval = -1;
641
 
      goto mandos_end;
 
446
    if (buffer_length + BUFFER_SIZE > buffer_capacity){
 
447
      buffer = realloc(buffer, buffer_capacity + BUFFER_SIZE);
 
448
      if (buffer == NULL){
 
449
        perror("realloc");
 
450
        goto exit;
 
451
      }
 
452
      buffer_capacity += BUFFER_SIZE;
642
453
    }
643
454
    
644
 
    sret = gnutls_record_recv(session, buffer+buffer_length,
645
 
                              BUFFER_SIZE);
646
 
    if(sret == 0){
 
455
    ret = gnutls_record_recv
 
456
      (es.session, buffer+buffer_length, BUFFER_SIZE);
 
457
    if (ret == 0){
647
458
      break;
648
459
    }
649
 
    if(sret < 0){
650
 
      switch(sret){
 
460
    if (ret < 0){
 
461
      switch(ret){
651
462
      case GNUTLS_E_INTERRUPTED:
652
463
      case GNUTLS_E_AGAIN:
653
464
        break;
654
465
      case GNUTLS_E_REHANDSHAKE:
655
 
        do{
656
 
          ret = gnutls_handshake(session);
657
 
        } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
658
 
        if(ret < 0){
659
 
          fprintf(stderr, "*** GnuTLS Re-handshake failed ***\n");
660
 
          gnutls_perror(ret);
 
466
        ret = gnutls_handshake (es.session);
 
467
        if (ret < 0){
 
468
          fprintf(stderr, "\n*** Handshake failed ***\n");
 
469
          gnutls_perror (ret);
661
470
          retval = -1;
662
 
          goto mandos_end;
 
471
          goto exit;
663
472
        }
664
473
        break;
665
474
      default:
666
475
        fprintf(stderr, "Unknown error while reading data from"
667
 
                " encrypted session with Mandos server\n");
 
476
                " encrypted session with mandos server\n");
668
477
        retval = -1;
669
 
        gnutls_bye(session, GNUTLS_SHUT_RDWR);
670
 
        goto mandos_end;
 
478
        gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
 
479
        goto exit;
671
480
      }
672
481
    } else {
673
 
      buffer_length += (size_t) sret;
 
482
      buffer_length += (size_t) ret;
674
483
    }
675
484
  }
676
485
  
677
 
  if(debug){
678
 
    fprintf(stderr, "Closing TLS session\n");
679
 
  }
680
 
  
681
 
  gnutls_bye(session, GNUTLS_SHUT_RDWR);
682
 
  
683
 
  if(buffer_length > 0){
684
 
    decrypted_buffer_size = pgp_packet_decrypt(mc, buffer,
 
486
  if (buffer_length > 0){
 
487
    decrypted_buffer_size = pgp_packet_decrypt(buffer,
685
488
                                               buffer_length,
686
 
                                               &decrypted_buffer);
687
 
    if(decrypted_buffer_size >= 0){
688
 
      written = 0;
689
 
      while(written < (size_t) decrypted_buffer_size){
690
 
        ret = (int)fwrite(decrypted_buffer + written, 1,
691
 
                          (size_t)decrypted_buffer_size - written,
692
 
                          stdout);
 
489
                                               &decrypted_buffer,
 
490
                                               certdir);
 
491
    if (decrypted_buffer_size >= 0){
 
492
      while(written < (size_t)decrypted_buffer_size){
 
493
        ret = (int)fwrite (decrypted_buffer + written, 1,
 
494
                           (size_t)decrypted_buffer_size - written,
 
495
                           stdout);
693
496
        if(ret == 0 and ferror(stdout)){
694
497
          if(debug){
695
498
            fprintf(stderr, "Error writing encrypted data: %s\n",
704
507
    } else {
705
508
      retval = -1;
706
509
    }
707
 
  } else {
708
 
    retval = -1;
709
 
  }
710
 
  
711
 
  /* Shutdown procedure */
712
 
  
713
 
 mandos_end:
 
510
  }
 
511
 
 
512
  //shutdown procedure
 
513
 
 
514
  if(debug){
 
515
    fprintf(stderr, "Closing TLS session\n");
 
516
  }
 
517
 
714
518
  free(buffer);
715
 
  ret = (int)TEMP_FAILURE_RETRY(close(tcp_sd));
716
 
  if(ret == -1){
717
 
    perror("close");
718
 
  }
719
 
  gnutls_deinit(session);
 
519
  gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
 
520
 exit:
 
521
  close(tcp_sd);
 
522
  gnutls_deinit (es.session);
 
523
  gnutls_certificate_free_credentials (es.cred);
 
524
  gnutls_global_deinit ();
720
525
  return retval;
721
526
}
722
527
 
723
 
static void resolve_callback(AvahiSServiceResolver *r,
724
 
                             AvahiIfIndex interface,
725
 
                             AVAHI_GCC_UNUSED AvahiProtocol protocol,
726
 
                             AvahiResolverEvent event,
727
 
                             const char *name,
728
 
                             const char *type,
729
 
                             const char *domain,
730
 
                             const char *host_name,
731
 
                             const AvahiAddress *address,
732
 
                             uint16_t port,
733
 
                             AVAHI_GCC_UNUSED AvahiStringList *txt,
734
 
                             AVAHI_GCC_UNUSED AvahiLookupResultFlags
735
 
                             flags,
736
 
                             void* userdata) {
737
 
  mandos_context *mc = userdata;
738
 
  assert(r);
 
528
static AvahiSimplePoll *simple_poll = NULL;
 
529
static AvahiServer *server = NULL;
 
530
 
 
531
static void resolve_callback(
 
532
    AvahiSServiceResolver *r,
 
533
    AvahiIfIndex interface,
 
534
    AVAHI_GCC_UNUSED AvahiProtocol protocol,
 
535
    AvahiResolverEvent event,
 
536
    const char *name,
 
537
    const char *type,
 
538
    const char *domain,
 
539
    const char *host_name,
 
540
    const AvahiAddress *address,
 
541
    uint16_t port,
 
542
    AVAHI_GCC_UNUSED AvahiStringList *txt,
 
543
    AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
 
544
    AVAHI_GCC_UNUSED void* userdata) {
 
545
    
 
546
  assert(r);                    /* Spurious warning */
739
547
  
740
548
  /* Called whenever a service has been resolved successfully or
741
549
     timed out */
742
550
  
743
 
  switch(event) {
 
551
  switch (event) {
744
552
  default:
745
553
  case AVAHI_RESOLVER_FAILURE:
746
 
    fprintf(stderr, "(Avahi Resolver) Failed to resolve service '%s'"
747
 
            " of type '%s' in domain '%s': %s\n", name, type, domain,
748
 
            avahi_strerror(avahi_server_errno(mc->server)));
 
554
    fprintf(stderr, "(Resolver) Failed to resolve service '%s' of"
 
555
            " type '%s' in domain '%s': %s\n", name, type, domain,
 
556
            avahi_strerror(avahi_server_errno(server)));
749
557
    break;
750
558
    
751
559
  case AVAHI_RESOLVER_FOUND:
753
561
      char ip[AVAHI_ADDRESS_STR_MAX];
754
562
      avahi_address_snprint(ip, sizeof(ip), address);
755
563
      if(debug){
756
 
        fprintf(stderr, "Mandos server \"%s\" found on %s (%s, %"
757
 
                PRIdMAX ") on port %" PRIu16 "\n", name, host_name,
758
 
                ip, (intmax_t)interface, port);
 
564
        fprintf(stderr, "Mandos server \"%s\" found on %s (%s) on"
 
565
                " port %d\n", name, host_name, ip, port);
759
566
      }
760
 
      int ret = start_mandos_communication(ip, port, interface, mc);
761
 
      if(ret == 0){
762
 
        avahi_simple_poll_quit(mc->simple_poll);
 
567
      int ret = start_mandos_communication(ip, port,
 
568
                                           (unsigned int) interface);
 
569
      if (ret == 0){
 
570
        exit(EXIT_SUCCESS);
763
571
      }
764
572
    }
765
573
  }
766
574
  avahi_s_service_resolver_free(r);
767
575
}
768
576
 
769
 
static void browse_callback( AvahiSServiceBrowser *b,
770
 
                             AvahiIfIndex interface,
771
 
                             AvahiProtocol protocol,
772
 
                             AvahiBrowserEvent event,
773
 
                             const char *name,
774
 
                             const char *type,
775
 
                             const char *domain,
776
 
                             AVAHI_GCC_UNUSED AvahiLookupResultFlags
777
 
                             flags,
778
 
                             void* userdata) {
779
 
  mandos_context *mc = userdata;
780
 
  assert(b);
781
 
  
782
 
  /* Called whenever a new services becomes available on the LAN or
783
 
     is removed from the LAN */
784
 
  
785
 
  switch(event) {
786
 
  default:
787
 
  case AVAHI_BROWSER_FAILURE:
788
 
    
789
 
    fprintf(stderr, "(Avahi browser) %s\n",
790
 
            avahi_strerror(avahi_server_errno(mc->server)));
791
 
    avahi_simple_poll_quit(mc->simple_poll);
792
 
    return;
793
 
    
794
 
  case AVAHI_BROWSER_NEW:
795
 
    /* We ignore the returned Avahi resolver object. In the callback
796
 
       function we free it. If the Avahi server is terminated before
797
 
       the callback function is called the Avahi server will free the
798
 
       resolver for us. */
799
 
    
800
 
    if(!(avahi_s_service_resolver_new(mc->server, interface,
801
 
                                       protocol, name, type, domain,
802
 
                                       AVAHI_PROTO_INET6, 0,
803
 
                                       resolve_callback, mc)))
804
 
      fprintf(stderr, "Avahi: Failed to resolve service '%s': %s\n",
805
 
              name, avahi_strerror(avahi_server_errno(mc->server)));
806
 
    break;
807
 
    
808
 
  case AVAHI_BROWSER_REMOVE:
809
 
    break;
810
 
    
811
 
  case AVAHI_BROWSER_ALL_FOR_NOW:
812
 
  case AVAHI_BROWSER_CACHE_EXHAUSTED:
813
 
    if(debug){
814
 
      fprintf(stderr, "No Mandos server found, still searching...\n");
 
577
static void browse_callback(
 
578
    AvahiSServiceBrowser *b,
 
579
    AvahiIfIndex interface,
 
580
    AvahiProtocol protocol,
 
581
    AvahiBrowserEvent event,
 
582
    const char *name,
 
583
    const char *type,
 
584
    const char *domain,
 
585
    AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
 
586
    void* userdata) {
 
587
    
 
588
    AvahiServer *s = userdata;
 
589
    assert(b);                  /* Spurious warning */
 
590
    
 
591
    /* Called whenever a new services becomes available on the LAN or
 
592
       is removed from the LAN */
 
593
    
 
594
    switch (event) {
 
595
    default:
 
596
    case AVAHI_BROWSER_FAILURE:
 
597
      
 
598
      fprintf(stderr, "(Browser) %s\n",
 
599
              avahi_strerror(avahi_server_errno(server)));
 
600
      avahi_simple_poll_quit(simple_poll);
 
601
      return;
 
602
      
 
603
    case AVAHI_BROWSER_NEW:
 
604
      /* We ignore the returned resolver object. In the callback
 
605
         function we free it. If the server is terminated before
 
606
         the callback function is called the server will free
 
607
         the resolver for us. */
 
608
      
 
609
      if (!(avahi_s_service_resolver_new(s, interface, protocol, name,
 
610
                                         type, domain,
 
611
                                         AVAHI_PROTO_INET6, 0,
 
612
                                         resolve_callback, s)))
 
613
        fprintf(stderr, "Failed to resolve service '%s': %s\n", name,
 
614
                avahi_strerror(avahi_server_errno(s)));
 
615
      break;
 
616
      
 
617
    case AVAHI_BROWSER_REMOVE:
 
618
      break;
 
619
      
 
620
    case AVAHI_BROWSER_ALL_FOR_NOW:
 
621
    case AVAHI_BROWSER_CACHE_EXHAUSTED:
 
622
      break;
815
623
    }
816
 
    break;
817
 
  }
818
 
}
819
 
 
820
 
int main(int argc, char *argv[]){
 
624
}
 
625
 
 
626
/* combinds file name and path and returns the malloced new string. som sane checks could/should be added */
 
627
const char *combinepath(const char *first, const char *second){
 
628
  char *tmp;
 
629
  tmp = malloc(strlen(first) + strlen(second) + 2);
 
630
  if (tmp == NULL){
 
631
    perror("malloc");
 
632
    return NULL;
 
633
  }
 
634
  strcpy(tmp, first);
 
635
  if (first[0] != '\0' and first[strlen(first) - 1] != '/'){
 
636
    strcat(tmp, "/");
 
637
  }
 
638
  strcat(tmp, second);
 
639
  return tmp;
 
640
}
 
641
 
 
642
 
 
643
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
 
644
    AvahiServerConfig config;
821
645
    AvahiSServiceBrowser *sb = NULL;
822
646
    int error;
823
647
    int ret;
824
 
    intmax_t tmpmax;
825
 
    int numchars;
826
 
    int exitcode = EXIT_SUCCESS;
 
648
    int returncode = EXIT_SUCCESS;
827
649
    const char *interface = "eth0";
828
650
    struct ifreq network;
829
651
    int sd;
830
 
    uid_t uid;
831
 
    gid_t gid;
832
 
    char *connect_to = NULL;
833
 
    char tempdir[] = "/tmp/mandosXXXXXX";
834
 
    AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
835
 
    const char *seckey = PATHDIR "/" SECKEY;
836
 
    const char *pubkey = PATHDIR "/" PUBKEY;
837
 
    
838
 
    mandos_context mc = { .simple_poll = NULL, .server = NULL,
839
 
                          .dh_bits = 1024, .priority = "SECURE256"
840
 
                          ":!CTYPE-X.509:+CTYPE-OPENPGP" };
841
 
    bool gnutls_initalized = false;
842
 
    bool gpgme_initalized = false;
843
 
    
844
 
    {
845
 
      struct argp_option options[] = {
846
 
        { .name = "debug", .key = 128,
847
 
          .doc = "Debug mode", .group = 3 },
848
 
        { .name = "connect", .key = 'c',
849
 
          .arg = "ADDRESS:PORT",
850
 
          .doc = "Connect directly to a specific Mandos server",
851
 
          .group = 1 },
852
 
        { .name = "interface", .key = 'i',
853
 
          .arg = "NAME",
854
 
          .doc = "Interface that will be used to search for Mandos"
855
 
          " servers",
856
 
          .group = 1 },
857
 
        { .name = "seckey", .key = 's',
858
 
          .arg = "FILE",
859
 
          .doc = "OpenPGP secret key file base name",
860
 
          .group = 1 },
861
 
        { .name = "pubkey", .key = 'p',
862
 
          .arg = "FILE",
863
 
          .doc = "OpenPGP public key file base name",
864
 
          .group = 2 },
865
 
        { .name = "dh-bits", .key = 129,
866
 
          .arg = "BITS",
867
 
          .doc = "Bit length of the prime number used in the"
868
 
          " Diffie-Hellman key exchange",
869
 
          .group = 2 },
870
 
        { .name = "priority", .key = 130,
871
 
          .arg = "STRING",
872
 
          .doc = "GnuTLS priority string for the TLS handshake",
873
 
          .group = 1 },
874
 
        { .name = NULL }
875
 
      };
876
 
      
877
 
      error_t parse_opt(int key, char *arg,
878
 
                        struct argp_state *state) {
879
 
        switch(key) {
880
 
        case 128:               /* --debug */
881
 
          debug = true;
882
 
          break;
883
 
        case 'c':               /* --connect */
884
 
          connect_to = arg;
885
 
          break;
886
 
        case 'i':               /* --interface */
887
 
          interface = arg;
888
 
          break;
889
 
        case 's':               /* --seckey */
890
 
          seckey = arg;
891
 
          break;
892
 
        case 'p':               /* --pubkey */
893
 
          pubkey = arg;
894
 
          break;
895
 
        case 129:               /* --dh-bits */
896
 
          ret = sscanf(arg, "%" SCNdMAX "%n", &tmpmax, &numchars);
897
 
          if(ret < 1 or tmpmax != (typeof(mc.dh_bits))tmpmax
898
 
             or arg[numchars] != '\0'){
899
 
            fprintf(stderr, "Bad number of DH bits\n");
900
 
            exit(EXIT_FAILURE);
901
 
          }
902
 
          mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
903
 
          break;
904
 
        case 130:               /* --priority */
905
 
          mc.priority = arg;
906
 
          break;
907
 
        case ARGP_KEY_ARG:
908
 
          argp_usage(state);
909
 
        case ARGP_KEY_END:
910
 
          break;
911
 
        default:
912
 
          return ARGP_ERR_UNKNOWN;
913
 
        }
914
 
        return 0;
915
 
      }
916
 
      
917
 
      struct argp argp = { .options = options, .parser = parse_opt,
918
 
                           .args_doc = "",
919
 
                           .doc = "Mandos client -- Get and decrypt"
920
 
                           " passwords from a Mandos server" };
921
 
      ret = argp_parse(&argp, argc, argv, 0, 0, NULL);
922
 
      if(ret == ARGP_ERR_UNKNOWN){
923
 
        fprintf(stderr, "Unknown error while parsing arguments\n");
924
 
        exitcode = EXIT_FAILURE;
925
 
        goto end;
926
 
      }
927
 
    }
928
 
    
929
 
    /* If the interface is down, bring it up */
930
 
    {
931
 
      sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
932
 
      if(sd < 0) {
933
 
        perror("socket");
934
 
        exitcode = EXIT_FAILURE;
935
 
        goto end;
936
 
      }
937
 
      strcpy(network.ifr_name, interface);
938
 
      ret = ioctl(sd, SIOCGIFFLAGS, &network);
939
 
      if(ret == -1){
940
 
        perror("ioctl SIOCGIFFLAGS");
941
 
        exitcode = EXIT_FAILURE;
942
 
        goto end;
943
 
      }
944
 
      if((network.ifr_flags & IFF_UP) == 0){
945
 
        network.ifr_flags |= IFF_UP;
946
 
        ret = ioctl(sd, SIOCSIFFLAGS, &network);
947
 
        if(ret == -1){
948
 
          perror("ioctl SIOCSIFFLAGS");
949
 
          exitcode = EXIT_FAILURE;
950
 
          goto end;
951
 
        }
952
 
      }
953
 
      ret = (int)TEMP_FAILURE_RETRY(close(sd));
954
 
      if(ret == -1){
955
 
        perror("close");
956
 
      }
957
 
    }
958
 
    
959
 
    uid = getuid();
960
 
    gid = getgid();
961
 
    
962
 
    ret = setuid(uid);
963
 
    if(ret == -1){
964
 
      perror("setuid");
965
 
    }
966
 
    
967
 
    setgid(gid);
968
 
    if(ret == -1){
969
 
      perror("setgid");
970
 
    }
971
 
    
972
 
    ret = init_gnutls_global(&mc, pubkey, seckey);
973
 
    if(ret == -1){
974
 
      fprintf(stderr, "init_gnutls_global failed\n");
975
 
      exitcode = EXIT_FAILURE;
976
 
      goto end;
977
 
    } else {
978
 
      gnutls_initalized = true;
979
 
    }
980
 
    
981
 
    if(mkdtemp(tempdir) == NULL){
982
 
      perror("mkdtemp");
983
 
      tempdir[0] = '\0';
984
 
      goto end;
985
 
    }
986
 
    
987
 
    if(not init_gpgme(&mc, pubkey, seckey, tempdir)){
988
 
      fprintf(stderr, "gpgme_initalized failed\n");
989
 
      exitcode = EXIT_FAILURE;
990
 
      goto end;
991
 
    } else {
992
 
      gpgme_initalized = true;
993
 
    }
994
 
    
995
 
    if_index = (AvahiIfIndex) if_nametoindex(interface);
996
 
    if(if_index == 0){
997
 
      fprintf(stderr, "No such interface: \"%s\"\n", interface);
998
 
      exit(EXIT_FAILURE);
999
 
    }
1000
 
    
1001
 
    if(connect_to != NULL){
1002
 
      /* Connect directly, do not use Zeroconf */
1003
 
      /* (Mainly meant for debugging) */
1004
 
      char *address = strrchr(connect_to, ':');
1005
 
      if(address == NULL){
1006
 
        fprintf(stderr, "No colon in address\n");
1007
 
        exitcode = EXIT_FAILURE;
1008
 
        goto end;
1009
 
      }
1010
 
      uint16_t port;
1011
 
      ret = sscanf(address+1, "%" SCNdMAX "%n", &tmpmax, &numchars);
1012
 
      if(ret < 1 or tmpmax != (uint16_t)tmpmax
1013
 
         or address[numchars+1] != '\0'){
1014
 
        fprintf(stderr, "Bad port number\n");
1015
 
        exitcode = EXIT_FAILURE;
1016
 
        goto end;
1017
 
      }
1018
 
      port = (uint16_t)tmpmax;
1019
 
      *address = '\0';
1020
 
      address = connect_to;
1021
 
      ret = start_mandos_communication(address, port, if_index, &mc);
1022
 
      if(ret < 0){
1023
 
        exitcode = EXIT_FAILURE;
1024
 
      } else {
1025
 
        exitcode = EXIT_SUCCESS;
1026
 
      }
1027
 
      goto end;
1028
 
    }
1029
 
    
1030
 
    if(not debug){
 
652
    
 
653
    while (true){
 
654
      static struct option long_options[] = {
 
655
        {"debug", no_argument, (int *)&debug, 1},
 
656
        {"interface", required_argument, 0, 'i'},
 
657
        {"certdir", required_argument, 0, 'd'},
 
658
        {"certkey", required_argument, 0, 'c'},
 
659
        {"certfile", required_argument, 0, 'k'},
 
660
        {0, 0, 0, 0} };
 
661
      
 
662
      int option_index = 0;
 
663
      ret = getopt_long (argc, argv, "i:", long_options,
 
664
                         &option_index);
 
665
      
 
666
      if (ret == -1){
 
667
        break;
 
668
      }
 
669
      
 
670
      switch(ret){
 
671
      case 0:
 
672
        break;
 
673
      case 'i':
 
674
        interface = optarg;
 
675
        break;
 
676
      case 'd':
 
677
        certdir = optarg;
 
678
        break;
 
679
      case 'c':
 
680
        certfile = optarg;
 
681
        break;
 
682
      case 'k':
 
683
        certkey = optarg;
 
684
        break;
 
685
      default:
 
686
        exit(EXIT_FAILURE);
 
687
      }
 
688
    }
 
689
 
 
690
    certfile = combinepath(certdir, certfile);
 
691
    if (certfile == NULL){
 
692
      returncode = EXIT_FAILURE;
 
693
      goto exit;
 
694
    }
 
695
    
 
696
    certkey = combinepath(certdir, certkey);
 
697
    if (certkey == NULL){
 
698
      returncode = EXIT_FAILURE;
 
699
      goto exit;
 
700
    }
 
701
 
 
702
    sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
 
703
    if(sd < 0) {
 
704
      perror("socket");
 
705
      returncode = EXIT_FAILURE;
 
706
      goto exit;
 
707
    }
 
708
    strcpy(network.ifr_name, interface);    
 
709
    ret = ioctl(sd, SIOCGIFFLAGS, &network);
 
710
    if(ret == -1){
 
711
      
 
712
      perror("ioctl SIOCGIFFLAGS");
 
713
      returncode = EXIT_FAILURE;
 
714
      goto exit;
 
715
    }
 
716
    if((network.ifr_flags & IFF_UP) == 0){
 
717
      network.ifr_flags |= IFF_UP;
 
718
      ret = ioctl(sd, SIOCSIFFLAGS, &network);
 
719
      if(ret == -1){
 
720
        perror("ioctl SIOCSIFFLAGS");
 
721
        returncode = EXIT_FAILURE;
 
722
        goto exit;
 
723
      }
 
724
    }
 
725
    close(sd);
 
726
    
 
727
    if (not debug){
1031
728
      avahi_set_log_function(empty_log);
1032
729
    }
1033
730
    
1034
 
    /* Initialize the pseudo-RNG for Avahi */
 
731
    /* Initialize the psuedo-RNG */
1035
732
    srand((unsigned int) time(NULL));
1036
 
    
1037
 
    /* Allocate main Avahi loop object */
1038
 
    mc.simple_poll = avahi_simple_poll_new();
1039
 
    if(mc.simple_poll == NULL) {
1040
 
        fprintf(stderr, "Avahi: Failed to create simple poll"
1041
 
                " object.\n");
1042
 
        exitcode = EXIT_FAILURE;
1043
 
        goto end;
1044
 
    }
1045
 
    
1046
 
    {
1047
 
      AvahiServerConfig config;
1048
 
      /* Do not publish any local Zeroconf records */
1049
 
      avahi_server_config_init(&config);
1050
 
      config.publish_hinfo = 0;
1051
 
      config.publish_addresses = 0;
1052
 
      config.publish_workstation = 0;
1053
 
      config.publish_domain = 0;
1054
 
      
1055
 
      /* Allocate a new server */
1056
 
      mc.server = avahi_server_new(avahi_simple_poll_get
1057
 
                                   (mc.simple_poll), &config, NULL,
1058
 
                                   NULL, &error);
1059
 
      
1060
 
      /* Free the Avahi configuration data */
1061
 
      avahi_server_config_free(&config);
1062
 
    }
1063
 
    
1064
 
    /* Check if creating the Avahi server object succeeded */
1065
 
    if(mc.server == NULL) {
1066
 
        fprintf(stderr, "Failed to create Avahi server: %s\n",
 
733
 
 
734
    /* Allocate main loop object */
 
735
    if (!(simple_poll = avahi_simple_poll_new())) {
 
736
        fprintf(stderr, "Failed to create simple poll object.\n");
 
737
        returncode = EXIT_FAILURE;      
 
738
        goto exit;
 
739
    }
 
740
 
 
741
    /* Do not publish any local records */
 
742
    avahi_server_config_init(&config);
 
743
    config.publish_hinfo = 0;
 
744
    config.publish_addresses = 0;
 
745
    config.publish_workstation = 0;
 
746
    config.publish_domain = 0;
 
747
 
 
748
    /* Allocate a new server */
 
749
    server = avahi_server_new(avahi_simple_poll_get(simple_poll),
 
750
                              &config, NULL, NULL, &error);
 
751
 
 
752
    /* Free the configuration data */
 
753
    avahi_server_config_free(&config);
 
754
 
 
755
    /* Check if creating the server object succeeded */
 
756
    if (!server) {
 
757
        fprintf(stderr, "Failed to create server: %s\n",
1067
758
                avahi_strerror(error));
1068
 
        exitcode = EXIT_FAILURE;
1069
 
        goto end;
 
759
        returncode = EXIT_FAILURE;
 
760
        goto exit;
1070
761
    }
1071
762
    
1072
 
    /* Create the Avahi service browser */
1073
 
    sb = avahi_s_service_browser_new(mc.server, if_index,
 
763
    /* Create the service browser */
 
764
    sb = avahi_s_service_browser_new(server,
 
765
                                     (AvahiIfIndex)
 
766
                                     if_nametoindex(interface),
1074
767
                                     AVAHI_PROTO_INET6,
1075
768
                                     "_mandos._tcp", NULL, 0,
1076
 
                                     browse_callback, &mc);
1077
 
    if(sb == NULL) {
 
769
                                     browse_callback, server);
 
770
    if (!sb) {
1078
771
        fprintf(stderr, "Failed to create service browser: %s\n",
1079
 
                avahi_strerror(avahi_server_errno(mc.server)));
1080
 
        exitcode = EXIT_FAILURE;
1081
 
        goto end;
 
772
                avahi_strerror(avahi_server_errno(server)));
 
773
        returncode = EXIT_FAILURE;
 
774
        goto exit;
1082
775
    }
1083
776
    
1084
777
    /* Run the main loop */
1085
 
    
1086
 
    if(debug){
1087
 
      fprintf(stderr, "Starting Avahi loop search\n");
 
778
 
 
779
    if (debug){
 
780
      fprintf(stderr, "Starting avahi loop search\n");
1088
781
    }
1089
782
    
1090
 
    avahi_simple_poll_loop(mc.simple_poll);
1091
 
    
1092
 
 end:
1093
 
    
1094
 
    if(debug){
 
783
    avahi_simple_poll_loop(simple_poll);
 
784
    
 
785
 exit:
 
786
 
 
787
    if (debug){
1095
788
      fprintf(stderr, "%s exiting\n", argv[0]);
1096
789
    }
1097
790
    
1098
791
    /* Cleanup things */
1099
 
    if(sb != NULL)
 
792
    if (sb)
1100
793
        avahi_s_service_browser_free(sb);
1101
794
    
1102
 
    if(mc.server != NULL)
1103
 
        avahi_server_free(mc.server);
1104
 
    
1105
 
    if(mc.simple_poll != NULL)
1106
 
        avahi_simple_poll_free(mc.simple_poll);
1107
 
    
1108
 
    if(gnutls_initalized){
1109
 
      gnutls_certificate_free_credentials(mc.cred);
1110
 
      gnutls_global_deinit();
1111
 
      gnutls_dh_params_deinit(mc.dh_params);
1112
 
    }
1113
 
    
1114
 
    if(gpgme_initalized){
1115
 
      gpgme_release(mc.ctx);
1116
 
    }
1117
 
    
1118
 
    /* Removes the temp directory used by GPGME */
1119
 
    if(tempdir[0] != '\0'){
1120
 
      DIR *d;
1121
 
      struct dirent *direntry;
1122
 
      d = opendir(tempdir);
1123
 
      if(d == NULL){
1124
 
        if(errno != ENOENT){
1125
 
          perror("opendir");
1126
 
        }
1127
 
      } else {
1128
 
        while(true){
1129
 
          direntry = readdir(d);
1130
 
          if(direntry == NULL){
1131
 
            break;
1132
 
          }
1133
 
          if(direntry->d_type == DT_REG){
1134
 
            char *fullname = NULL;
1135
 
            ret = asprintf(&fullname, "%s/%s", tempdir,
1136
 
                           direntry->d_name);
1137
 
            if(ret < 0){
1138
 
              perror("asprintf");
1139
 
              continue;
1140
 
            }
1141
 
            ret = unlink(fullname);
1142
 
            if(ret == -1){
1143
 
              fprintf(stderr, "unlink(\"%s\"): %s",
1144
 
                      fullname, strerror(errno));
1145
 
            }
1146
 
            free(fullname);
1147
 
          }
1148
 
        }
1149
 
        closedir(d);
1150
 
      }
1151
 
      ret = rmdir(tempdir);
1152
 
      if(ret == -1 and errno != ENOENT){
1153
 
        perror("rmdir");
1154
 
      }
1155
 
    }
1156
 
          
1157
 
    return exitcode;
 
795
    if (server)
 
796
        avahi_server_free(server);
 
797
 
 
798
    if (simple_poll)
 
799
        avahi_simple_poll_free(simple_poll);
 
800
    free(certfile);
 
801
    free(certkey);
 
802
    
 
803
    return returncode;
1158
804
}