/mandos/release

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/release

« back to all changes in this revision

Viewing changes to plugins.d/mandos-client.c

* INSTALL (Prerequisites/Mandos Server): Added "PyGObject".

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