32
33
#define _LARGEFILE_SOURCE
33
34
#define _FILE_OFFSET_BITS 64
39
#include <net/if.h> /* if_nametoindex */
40
#include <sys/ioctl.h> // ioctl, ifreq, SIOCGIFFLAGS, IFF_UP, SIOCSIFFLAGS
41
#include <net/if.h> // ioctl, ifreq, SIOCGIFFLAGS, IFF_UP, SIOCSIFFLAGS
36
#define _GNU_SOURCE /* TEMP_FAILURE_RETRY(), asprintf() */
38
#include <stdio.h> /* fprintf(), stderr, fwrite(),
39
stdout, ferror(), sscanf(),
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,
45
#include <stdbool.h> /* bool, false, 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, uid_t, gid_t, open(),
53
#include <sys/stat.h> /* open() */
54
#include <sys/socket.h> /* socket(), struct sockaddr_in6,
55
inet_pton(), connect() */
56
#include <fcntl.h> /* open() */
57
#include <dirent.h> /* opendir(), struct dirent, readdir()
59
#include <inttypes.h> /* PRIu16, intmax_t, SCNdMAX */
60
#include <assert.h> /* assert() */
61
#include <errno.h> /* perror(), errno */
62
#include <time.h> /* nanosleep(), time() */
63
#include <net/if.h> /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
64
SIOCSIFFLAGS, if_indextoname(),
65
if_nametoindex(), IF_NAMESIZE */
66
#include <netinet/in.h> /* IN6_IS_ADDR_LINKLOCAL,
67
INET_ADDRSTRLEN, INET6_ADDRSTRLEN
69
#include <unistd.h> /* close(), SEEK_SET, off_t, write(),
70
getuid(), getgid(), setuid(),
72
#include <arpa/inet.h> /* inet_pton(), htons */
73
#include <iso646.h> /* not, or, and */
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 */
79
#include <sys/klog.h> /* klogctl() */
83
/* All Avahi types, constants and functions
43
86
#include <avahi-core/core.h>
44
87
#include <avahi-core/lookup.h>
45
88
#include <avahi-core/log.h>
47
90
#include <avahi-common/malloc.h>
48
91
#include <avahi-common/error.h>
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 */
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 */
65
#include <errno.h> /* perror() */
94
#include <gnutls/gnutls.h> /* All GnuTLS types, constants and
97
init_gnutls_session(),
99
#include <gnutls/openpgp.h>
100
/* gnutls_certificate_set_openpgp_key_file(),
101
GNUTLS_OPENPGP_FMT_BASE64 */
104
#include <gpgme.h> /* All GPGME types, constants and
107
GPGME_PROTOCOL_OpenPGP,
71
110
#define BUFFER_SIZE 256
73
static const char *keydir = "/conf/conf.d/mandos";
74
static const char *pubkeyfile = "pubkey.txt";
75
static const char *seckeyfile = "seckey.txt";
112
#define PATHDIR "/conf/conf.d/mandos"
113
#define SECKEY "seckey.txt"
114
#define PUBKEY "pubkey.txt"
77
116
bool debug = false;
117
static const char mandos_protocol_version[] = "1";
118
const char *argp_program_version = "mandos-client " VERSION;
119
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
79
/* Used for passing in values through all the callback functions */
121
/* Used for passing in values through the Avahi callback functions */
81
123
AvahiSimplePoll *simple_poll;
82
124
AvahiServer *server;
83
125
gnutls_certificate_credentials_t cred;
84
126
unsigned int dh_bits;
127
gnutls_dh_params_t dh_params;
85
128
const char *priority;
88
static ssize_t pgp_packet_decrypt (char *packet, size_t packet_size,
91
gpgme_data_t dh_crypto, dh_plain;
133
* Make room in "buffer" for at least BUFFER_SIZE additional bytes.
134
* "buffer_capacity" is how much is currently allocated,
135
* "buffer_length" is how much is already used.
137
size_t adjustbuffer(char **buffer, size_t buffer_length,
138
size_t buffer_capacity){
139
if(buffer_length + BUFFER_SIZE > buffer_capacity){
140
*buffer = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
144
buffer_capacity += BUFFER_SIZE;
146
return buffer_capacity;
152
static bool init_gpgme(mandos_context *mc, const char *seckey,
153
const char *pubkey, const char *tempdir){
95
ssize_t new_packet_capacity = 0;
96
ssize_t new_packet_length = 0;
97
156
gpgme_engine_info_t engine_info;
100
fprintf(stderr, "Trying to decrypt OpenPGP packet\n");
160
* Helper function to insert pub and seckey to the engine keyring.
162
bool import_key(const char *filename){
164
gpgme_data_t pgp_data;
166
fd = (int)TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
172
rc = gpgme_data_new_from_fd(&pgp_data, fd);
173
if(rc != GPG_ERR_NO_ERROR){
174
fprintf(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
175
gpgme_strsource(rc), gpgme_strerror(rc));
179
rc = gpgme_op_import(mc->ctx, pgp_data);
180
if(rc != GPG_ERR_NO_ERROR){
181
fprintf(stderr, "bad gpgme_op_import: %s: %s\n",
182
gpgme_strsource(rc), gpgme_strerror(rc));
186
ret = (int)TEMP_FAILURE_RETRY(close(fd));
190
gpgme_data_release(pgp_data);
195
fprintf(stderr, "Initialize gpgme\n");
104
199
gpgme_check_version(NULL);
105
200
rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
106
if (rc != GPG_ERR_NO_ERROR){
201
if(rc != GPG_ERR_NO_ERROR){
107
202
fprintf(stderr, "bad gpgme_engine_check_version: %s: %s\n",
108
203
gpgme_strsource(rc), gpgme_strerror(rc));
112
/* Set GPGME home directory */
113
rc = gpgme_get_engine_info (&engine_info);
114
if (rc != GPG_ERR_NO_ERROR){
207
/* Set GPGME home directory for the OpenPGP engine only */
208
rc = gpgme_get_engine_info(&engine_info);
209
if(rc != GPG_ERR_NO_ERROR){
115
210
fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
116
211
gpgme_strsource(rc), gpgme_strerror(rc));
119
214
while(engine_info != NULL){
120
215
if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
121
216
gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
122
engine_info->file_name, homedir);
217
engine_info->file_name, tempdir);
125
220
engine_info = engine_info->next;
127
222
if(engine_info == NULL){
128
fprintf(stderr, "Could not set home dir to %s\n", homedir);
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){
223
fprintf(stderr, "Could not set GPGME home dir to %s\n", tempdir);
227
/* Create new GPGME "context" */
228
rc = gpgme_new(&(mc->ctx));
229
if(rc != GPG_ERR_NO_ERROR){
230
fprintf(stderr, "bad gpgme_new: %s: %s\n",
231
gpgme_strsource(rc), gpgme_strerror(rc));
235
if(not import_key(pubkey) or not import_key(seckey)){
243
* Decrypt OpenPGP data.
244
* Returns -1 on error
246
static ssize_t pgp_packet_decrypt(const mandos_context *mc,
247
const char *cryptotext,
250
gpgme_data_t dh_crypto, dh_plain;
253
size_t plaintext_capacity = 0;
254
ssize_t plaintext_length = 0;
257
fprintf(stderr, "Trying to decrypt OpenPGP data\n");
260
/* Create new GPGME data buffer from memory cryptotext */
261
rc = gpgme_data_new_from_mem(&dh_crypto, cryptotext, crypto_size,
263
if(rc != GPG_ERR_NO_ERROR){
135
264
fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
136
265
gpgme_strsource(rc), gpgme_strerror(rc));
140
269
/* Create new empty GPGME data buffer for the plaintext */
141
270
rc = gpgme_data_new(&dh_plain);
142
if (rc != GPG_ERR_NO_ERROR){
271
if(rc != GPG_ERR_NO_ERROR){
143
272
fprintf(stderr, "bad gpgme_data_new: %s: %s\n",
144
273
gpgme_strsource(rc), gpgme_strerror(rc));
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));
156
/* Decrypt data from the FILE pointer to the plaintext data
158
rc = gpgme_op_decrypt(ctx, dh_crypto, dh_plain);
159
if (rc != GPG_ERR_NO_ERROR){
274
gpgme_data_release(dh_crypto);
278
/* Decrypt data from the cryptotext data buffer to the plaintext
280
rc = gpgme_op_decrypt(mc->ctx, dh_crypto, dh_plain);
281
if(rc != GPG_ERR_NO_ERROR){
160
282
fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
161
283
gpgme_strsource(rc), gpgme_strerror(rc));
284
plaintext_length = -1;
286
gpgme_decrypt_result_t result;
287
result = gpgme_op_decrypt_result(mc->ctx);
289
fprintf(stderr, "gpgme_op_decrypt_result failed\n");
291
fprintf(stderr, "Unsupported algorithm: %s\n",
292
result->unsupported_algorithm);
293
fprintf(stderr, "Wrong key usage: %u\n",
294
result->wrong_key_usage);
295
if(result->file_name != NULL){
296
fprintf(stderr, "File name: %s\n", result->file_name);
298
gpgme_recipient_t recipient;
299
recipient = result->recipients;
301
while(recipient != NULL){
302
fprintf(stderr, "Public key algorithm: %s\n",
303
gpgme_pubkey_algo_name(recipient->pubkey_algo));
304
fprintf(stderr, "Key ID: %s\n", recipient->keyid);
305
fprintf(stderr, "Secret key available: %s\n",
306
recipient->status == GPG_ERR_NO_SECKEY
308
recipient = recipient->next;
166
fprintf(stderr, "Decryption of OpenPGP packet succeeded\n");
170
gpgme_decrypt_result_t result;
171
result = gpgme_op_decrypt_result(ctx);
173
fprintf(stderr, "gpgme_op_decrypt_result failed\n");
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);
182
gpgme_recipient_t recipient;
183
recipient = result->recipients;
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
192
recipient = recipient->next;
198
/* Delete the GPGME FILE pointer cryptotext data buffer */
199
gpgme_data_release(dh_crypto);
317
fprintf(stderr, "Decryption of OpenPGP data succeeded\n");
201
320
/* Seek back to the beginning of the GPGME plaintext data buffer */
202
if (gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET) == -1){
203
perror("pgpme_data_seek");
321
if(gpgme_data_seek(dh_plain, (off_t)0, SEEK_SET) == -1){
322
perror("gpgme_data_seek");
323
plaintext_length = -1;
208
if (new_packet_length + BUFFER_SIZE > new_packet_capacity){
209
*new_packet = realloc(*new_packet,
210
(unsigned int)new_packet_capacity
212
if (*new_packet == NULL){
216
new_packet_capacity += BUFFER_SIZE;
329
plaintext_capacity = adjustbuffer(plaintext,
330
(size_t)plaintext_length,
332
if(plaintext_capacity == 0){
333
perror("adjustbuffer");
334
plaintext_length = -1;
219
ret = gpgme_data_read(dh_plain, *new_packet + new_packet_length,
338
ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
221
340
/* Print the data, if any */
226
346
perror("gpgme_data_read");
229
new_packet_length += ret;
232
/* FIXME: check characters before printing to screen so to not print
233
terminal control characters */
235
/* fprintf(stderr, "decrypted password is: "); */
236
/* fwrite(*new_packet, 1, new_packet_length, stderr); */
237
/* fprintf(stderr, "\n"); */
347
plaintext_length = -1;
350
plaintext_length += ret;
354
fprintf(stderr, "Decrypted password is: ");
355
for(ssize_t i = 0; i < plaintext_length; i++){
356
fprintf(stderr, "%02hhX ", (*plaintext)[i]);
358
fprintf(stderr, "\n");
363
/* Delete the GPGME cryptotext data buffer */
364
gpgme_data_release(dh_crypto);
240
366
/* Delete the GPGME plaintext data buffer */
241
367
gpgme_data_release(dh_plain);
242
return new_packet_length;
368
return plaintext_length;
245
static const char * safer_gnutls_strerror (int value) {
246
const char *ret = gnutls_strerror (value);
371
static const char * safer_gnutls_strerror(int value){
372
const char *ret = gnutls_strerror(value); /* Spurious warning from
373
-Wunreachable-code */
248
375
ret = "(unknown)";
379
/* GnuTLS log function callback */
252
380
static void debuggnutls(__attribute__((unused)) int level,
253
381
const char* string){
254
fprintf(stderr, "%s", string);
382
fprintf(stderr, "GnuTLS: %s", string);
257
static int initgnutls(mandos_context *mc, gnutls_session_t *session,
258
gnutls_dh_params_t *dh_params){
385
static int init_gnutls_global(mandos_context *mc,
386
const char *pubkeyfilename,
387
const char *seckeyfilename){
263
391
fprintf(stderr, "Initializing GnuTLS\n");
266
if ((ret = gnutls_global_init ())
267
!= GNUTLS_E_SUCCESS) {
268
fprintf (stderr, "global_init: %s\n", safer_gnutls_strerror(ret));
394
ret = gnutls_global_init();
395
if(ret != GNUTLS_E_SUCCESS){
396
fprintf(stderr, "GnuTLS global_init: %s\n",
397
safer_gnutls_strerror(ret));
402
/* "Use a log level over 10 to enable all debugging options."
273
405
gnutls_global_set_log_level(11);
274
406
gnutls_global_set_log_function(debuggnutls);
277
/* openpgp credentials */
278
if ((ret = gnutls_certificate_allocate_credentials (&mc->cred))
279
!= GNUTLS_E_SUCCESS) {
280
fprintf (stderr, "memory error: %s\n",
281
safer_gnutls_strerror(ret));
409
/* OpenPGP credentials */
410
gnutls_certificate_allocate_credentials(&mc->cred);
411
if(ret != GNUTLS_E_SUCCESS){
412
fprintf(stderr, "GnuTLS memory error: %s\n", /* Spurious warning
416
safer_gnutls_strerror(ret));
417
gnutls_global_deinit();
286
fprintf(stderr, "Attempting to use OpenPGP certificate %s"
287
" and keyfile %s as GnuTLS credentials\n", pubkeyfile,
422
fprintf(stderr, "Attempting to use OpenPGP public key %s and"
423
" secret key %s as GnuTLS credentials\n", pubkeyfilename,
291
427
ret = gnutls_certificate_set_openpgp_key_file
292
(mc->cred, pubkeyfile, seckeyfile, GNUTLS_OPENPGP_FMT_BASE64);
293
if (ret != GNUTLS_E_SUCCESS) {
295
(stderr, "Error[%d] while reading the OpenPGP key pair ('%s',"
297
ret, pubkeyfile, seckeyfile);
298
fprintf(stdout, "The Error is: %s\n",
299
safer_gnutls_strerror(ret));
303
//GnuTLS server initialization
304
if ((ret = gnutls_dh_params_init(dh_params))
305
!= GNUTLS_E_SUCCESS) {
306
fprintf (stderr, "Error in dh parameter initialization: %s\n",
307
safer_gnutls_strerror(ret));
311
if ((ret = gnutls_dh_params_generate2(*dh_params, mc->dh_bits))
312
!= GNUTLS_E_SUCCESS) {
313
fprintf (stderr, "Error in prime generation: %s\n",
314
safer_gnutls_strerror(ret));
318
gnutls_certificate_set_dh_params(mc->cred, *dh_params);
320
// GnuTLS session creation
321
if ((ret = gnutls_init(session, GNUTLS_SERVER))
322
!= GNUTLS_E_SUCCESS){
428
(mc->cred, pubkeyfilename, seckeyfilename,
429
GNUTLS_OPENPGP_FMT_BASE64);
430
if(ret != GNUTLS_E_SUCCESS){
432
"Error[%d] while reading the OpenPGP key pair ('%s',"
433
" '%s')\n", ret, pubkeyfilename, seckeyfilename);
434
fprintf(stderr, "The GnuTLS error is: %s\n",
435
safer_gnutls_strerror(ret));
439
/* GnuTLS server initialization */
440
ret = gnutls_dh_params_init(&mc->dh_params);
441
if(ret != GNUTLS_E_SUCCESS){
442
fprintf(stderr, "Error in GnuTLS DH parameter initialization:"
443
" %s\n", safer_gnutls_strerror(ret));
446
ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
447
if(ret != GNUTLS_E_SUCCESS){
448
fprintf(stderr, "Error in GnuTLS prime generation: %s\n",
449
safer_gnutls_strerror(ret));
453
gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
459
gnutls_certificate_free_credentials(mc->cred);
460
gnutls_global_deinit();
461
gnutls_dh_params_deinit(mc->dh_params);
465
static int init_gnutls_session(mandos_context *mc,
466
gnutls_session_t *session){
468
/* GnuTLS session creation */
469
ret = gnutls_init(session, GNUTLS_SERVER);
470
if(ret != GNUTLS_E_SUCCESS){
323
471
fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
324
472
safer_gnutls_strerror(ret));
327
if ((ret = gnutls_priority_set_direct(*session, mc->priority, &err))
328
!= GNUTLS_E_SUCCESS) {
329
fprintf(stderr, "Syntax error at: %s\n", err);
330
fprintf(stderr, "GnuTLS error: %s\n",
331
safer_gnutls_strerror(ret));
477
ret = gnutls_priority_set_direct(*session, mc->priority, &err);
478
if(ret != GNUTLS_E_SUCCESS){
479
fprintf(stderr, "Syntax error at: %s\n", err);
480
fprintf(stderr, "GnuTLS error: %s\n",
481
safer_gnutls_strerror(ret));
482
gnutls_deinit(*session);
335
if ((ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
337
!= GNUTLS_E_SUCCESS) {
338
fprintf(stderr, "Error setting a credentials set: %s\n",
487
ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
489
if(ret != GNUTLS_E_SUCCESS){
490
fprintf(stderr, "Error setting GnuTLS credentials: %s\n",
339
491
safer_gnutls_strerror(ret));
492
gnutls_deinit(*session);
343
496
/* ignore client certificate if any. */
344
gnutls_certificate_server_set_request (*session,
497
gnutls_certificate_server_set_request(*session,
347
gnutls_dh_set_prime_bits (*session, mc->dh_bits);
500
gnutls_dh_set_prime_bits(*session, mc->dh_bits);
505
/* Avahi log function callback */
352
506
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
353
507
__attribute__((unused)) const char *txt){}
509
/* Called when a Mandos server is found */
355
510
static int start_mandos_communication(const char *ip, uint16_t port,
356
511
AvahiIfIndex if_index,
512
mandos_context *mc, int af){
359
struct sockaddr_in6 to;
516
struct sockaddr_in in;
517
struct sockaddr_in6 in6;
360
519
char *buffer = NULL;
361
520
char *decrypted_buffer;
362
521
size_t buffer_length = 0;
363
522
size_t buffer_capacity = 0;
364
523
ssize_t decrypted_buffer_size;
367
char interface[IF_NAMESIZE];
368
526
gnutls_session_t session;
369
gnutls_dh_params_t dh_params;
527
int pf; /* Protocol family */
537
fprintf(stderr, "Bad address family: %d\n", af);
541
ret = init_gnutls_session(mc, &session);
372
fprintf(stderr, "Setting up a tcp connection to %s, port %d\n",
547
fprintf(stderr, "Setting up a TCP connection to %s, port %" PRIu16
376
tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
551
tcp_sd = socket(pf, SOCK_STREAM, 0);
378
553
perror("socket");
383
if(if_indextoname((unsigned int)if_index, interface) == NULL){
384
perror("if_indextoname");
387
fprintf(stderr, "Binding to interface %s\n", interface);
390
memset(&to,0,sizeof(to)); /* Spurious warning */
391
to.sin6_family = AF_INET6;
392
ret = inet_pton(AF_INET6, ip, &to.sin6_addr);
557
memset(&to, 0, sizeof(to));
559
to.in6.sin6_family = (uint16_t)af;
560
ret = inet_pton(af, ip, &to.in6.sin6_addr);
562
to.in.sin_family = (sa_family_t)af;
563
ret = inet_pton(af, ip, &to.in.sin_addr);
394
566
perror("inet_pton");
418
ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
628
ret = connect(tcp_sd, &to.in6, sizeof(to));
630
ret = connect(tcp_sd, &to.in, sizeof(to)); /* IPv4 */
420
633
perror("connect");
424
ret = initgnutls (mc, &session, &dh_params);
637
const char *out = mandos_protocol_version;
640
size_t out_size = strlen(out);
641
ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
642
out_size - written));
648
written += (size_t)ret;
649
if(written < out_size){
652
if(out == mandos_protocol_version){
430
gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t) tcp_sd);
433
662
fprintf(stderr, "Establishing TLS session with %s\n", ip);
436
ret = gnutls_handshake (session);
438
if (ret != GNUTLS_E_SUCCESS){
665
gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t) tcp_sd);
668
ret = gnutls_handshake(session);
669
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
671
if(ret != GNUTLS_E_SUCCESS){
440
fprintf(stderr, "\n*** Handshake failed ***\n");
673
fprintf(stderr, "*** GnuTLS Handshake failed ***\n");
447
//Retrieve OpenPGP packet that contains the wanted password
680
/* Read OpenPGP packet that contains the wanted password */
450
fprintf(stderr, "Retrieving pgp encrypted password from %s\n",
683
fprintf(stderr, "Retrieving OpenPGP encrypted password from %s\n",
455
if (buffer_length + BUFFER_SIZE > buffer_capacity){
456
buffer = realloc(buffer, buffer_capacity + BUFFER_SIZE);
461
buffer_capacity += BUFFER_SIZE;
688
buffer_capacity = adjustbuffer(&buffer, buffer_length,
690
if(buffer_capacity == 0){
691
perror("adjustbuffer");
464
ret = gnutls_record_recv(session, buffer+buffer_length,
696
sret = gnutls_record_recv(session, buffer+buffer_length,
471
703
case GNUTLS_E_INTERRUPTED:
472
704
case GNUTLS_E_AGAIN:
474
706
case GNUTLS_E_REHANDSHAKE:
475
ret = gnutls_handshake (session);
477
fprintf(stderr, "\n*** Handshake failed ***\n");
708
ret = gnutls_handshake(session);
709
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
711
fprintf(stderr, "*** GnuTLS Re-handshake failed ***\n");
484
718
fprintf(stderr, "Unknown error while reading data from"
485
" encrypted session with mandos server\n");
719
" encrypted session with Mandos server\n");
487
gnutls_bye (session, GNUTLS_SHUT_RDWR);
721
gnutls_bye(session, GNUTLS_SHUT_RDWR);
491
buffer_length += (size_t) ret;
725
buffer_length += (size_t) sret;
495
if (buffer_length > 0){
496
decrypted_buffer_size = pgp_packet_decrypt(buffer,
730
fprintf(stderr, "Closing TLS session\n");
733
gnutls_bye(session, GNUTLS_SHUT_RDWR);
735
if(buffer_length > 0){
736
decrypted_buffer_size = pgp_packet_decrypt(mc, buffer,
500
if (decrypted_buffer_size >= 0){
739
if(decrypted_buffer_size >= 0){
501
741
while(written < (size_t) decrypted_buffer_size){
502
ret = (int)fwrite (decrypted_buffer + written, 1,
503
(size_t)decrypted_buffer_size - written,
742
ret = (int)fwrite(decrypted_buffer + written, 1,
743
(size_t)decrypted_buffer_size - written,
505
745
if(ret == 0 and ferror(stdout)){
507
747
fprintf(stderr, "Error writing encrypted data: %s\n",
566
805
char ip[AVAHI_ADDRESS_STR_MAX];
567
806
avahi_address_snprint(ip, sizeof(ip), address);
569
fprintf(stderr, "Mandos server \"%s\" found on %s (%s) on"
570
" port %d\n", name, host_name, ip, port);
808
fprintf(stderr, "Mandos server \"%s\" found on %s (%s, %"
809
PRIdMAX ") on port %" PRIu16 "\n", name, host_name,
810
ip, (intmax_t)interface, port);
572
int ret = start_mandos_communication(ip, port, interface, mc);
812
int ret = start_mandos_communication(ip, port, interface, mc,
813
avahi_proto_to_af(proto));
815
avahi_simple_poll_quit(mc->simple_poll);
578
819
avahi_s_service_resolver_free(r);
581
static void browse_callback( AvahiSServiceBrowser *b,
582
AvahiIfIndex interface,
583
AvahiProtocol protocol,
584
AvahiBrowserEvent event,
588
AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
822
static void browse_callback(AvahiSServiceBrowser *b,
823
AvahiIfIndex interface,
824
AvahiProtocol protocol,
825
AvahiBrowserEvent event,
829
AVAHI_GCC_UNUSED AvahiLookupResultFlags
590
832
mandos_context *mc = userdata;
591
assert(b); /* Spurious warning */
593
835
/* Called whenever a new services becomes available on the LAN or
594
836
is removed from the LAN */
598
840
case AVAHI_BROWSER_FAILURE:
600
fprintf(stderr, "(Browser) %s\n",
842
fprintf(stderr, "(Avahi browser) %s\n",
601
843
avahi_strerror(avahi_server_errno(mc->server)));
602
844
avahi_simple_poll_quit(mc->simple_poll);
605
847
case AVAHI_BROWSER_NEW:
606
/* We ignore the returned resolver object. In the callback
607
function we free it. If the server is terminated before
608
the callback function is called the server will free
609
the resolver for us. */
848
/* We ignore the returned Avahi resolver object. In the callback
849
function we free it. If the Avahi server is terminated before
850
the callback function is called the Avahi server will free the
611
if (!(avahi_s_service_resolver_new(mc->server, interface, protocol, name,
853
if(!(avahi_s_service_resolver_new(mc->server, interface,
854
protocol, name, type, domain,
613
855
AVAHI_PROTO_INET6, 0,
614
856
resolve_callback, mc)))
615
fprintf(stderr, "Failed to resolve service '%s': %s\n", name,
616
avahi_strerror(avahi_server_errno(mc->server)));
857
fprintf(stderr, "Avahi: Failed to resolve service '%s': %s\n",
858
name, avahi_strerror(avahi_server_errno(mc->server)));
619
861
case AVAHI_BROWSER_REMOVE:
622
864
case AVAHI_BROWSER_ALL_FOR_NOW:
623
865
case AVAHI_BROWSER_CACHE_EXHAUSTED:
867
fprintf(stderr, "No Mandos server found, still searching...\n");
628
/* Combines file name and path and returns the malloced new
629
string. some sane checks could/should be added */
630
static const char *combinepath(const char *first, const char *second){
631
size_t f_len = strlen(first);
632
size_t s_len = strlen(second);
633
char *tmp = malloc(f_len + s_len + 2);
638
memcpy(tmp, first, f_len); /* Spurious warning */
642
memcpy(tmp + f_len + 1, second, s_len); /* Spurious warning */
644
tmp[f_len + 1 + s_len] = '\0';
649
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
650
AvahiServerConfig config;
651
AvahiSServiceBrowser *sb = NULL;
655
int returncode = EXIT_SUCCESS;
656
const char *interface = "eth0";
657
struct ifreq network;
659
char *connect_to = NULL;
660
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
661
mandos_context mc = { .simple_poll = NULL, .server = NULL,
662
.dh_bits = 1024, .priority = "SECURE256"};
873
int main(int argc, char *argv[]){
874
AvahiSServiceBrowser *sb = NULL;
879
int exitcode = EXIT_SUCCESS;
880
const char *interface = "eth0";
881
struct ifreq network;
885
char *connect_to = NULL;
886
char tempdir[] = "/tmp/mandosXXXXXX";
887
bool tempdir_created = false;
888
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
889
const char *seckey = PATHDIR "/" SECKEY;
890
const char *pubkey = PATHDIR "/" PUBKEY;
892
mandos_context mc = { .simple_poll = NULL, .server = NULL,
893
.dh_bits = 1024, .priority = "SECURE256"
894
":!CTYPE-X.509:+CTYPE-OPENPGP" };
895
bool gnutls_initialized = false;
896
bool gpgme_initialized = false;
900
struct argp_option options[] = {
901
{ .name = "debug", .key = 128,
902
.doc = "Debug mode", .group = 3 },
903
{ .name = "connect", .key = 'c',
904
.arg = "ADDRESS:PORT",
905
.doc = "Connect directly to a specific Mandos server",
907
{ .name = "interface", .key = 'i',
909
.doc = "Network interface that will be used to search for"
912
{ .name = "seckey", .key = 's',
914
.doc = "OpenPGP secret key file base name",
916
{ .name = "pubkey", .key = 'p',
918
.doc = "OpenPGP public key file base name",
920
{ .name = "dh-bits", .key = 129,
922
.doc = "Bit length of the prime number used in the"
923
" Diffie-Hellman key exchange",
925
{ .name = "priority", .key = 130,
927
.doc = "GnuTLS priority string for the TLS handshake",
929
{ .name = "delay", .key = 131,
931
.doc = "Maximum delay to wait for interface startup",
664
debug_int = debug ? 1 : 0;
666
struct option long_options[] = {
667
{"debug", no_argument, &debug_int, 1},
668
{"connect", required_argument, NULL, 'c'},
669
{"interface", required_argument, NULL, 'i'},
670
{"keydir", required_argument, NULL, 'd'},
671
{"seckey", required_argument, NULL, 's'},
672
{"pubkey", required_argument, NULL, 'p'},
673
{"dh-bits", required_argument, NULL, 'D'},
674
{"priority", required_argument, NULL, 'P'},
677
int option_index = 0;
678
ret = getopt_long (argc, argv, "i:", long_options,
705
mc.dh_bits = (unsigned int) strtol(optarg, NULL, 10);
712
mc.priority = optarg;
936
error_t parse_opt(int key, char *arg,
937
struct argp_state *state){
939
case 128: /* --debug */
942
case 'c': /* --connect */
945
case 'i': /* --interface */
948
case 's': /* --seckey */
951
case 'p': /* --pubkey */
954
case 129: /* --dh-bits */
955
ret = sscanf(arg, "%" SCNdMAX "%n", &tmpmax, &numchars);
956
if(ret < 1 or tmpmax != (typeof(mc.dh_bits))tmpmax
957
or arg[numchars] != '\0'){
958
fprintf(stderr, "Bad number of DH bits\n");
961
mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
963
case 130: /* --priority */
966
case 131: /* --delay */
967
ret = sscanf(arg, "%lf%n", &delay, &numchars);
968
if(ret < 1 or arg[numchars] != '\0'){
969
fprintf(stderr, "Bad delay\n");
719
debug = debug_int ? true : false;
721
pubkeyfile = combinepath(keydir, pubkeyfile);
722
if (pubkeyfile == NULL){
723
perror("combinepath");
724
returncode = EXIT_FAILURE;
728
seckeyfile = combinepath(keydir, seckeyfile);
729
if (seckeyfile == NULL){
730
perror("combinepath");
734
if_index = (AvahiIfIndex) if_nametoindex(interface);
736
fprintf(stderr, "No such interface: \"%s\"\n", interface);
740
if(connect_to != NULL){
741
/* Connect directly, do not use Zeroconf */
742
/* (Mainly meant for debugging) */
743
char *address = strrchr(connect_to, ':');
745
fprintf(stderr, "No colon in address\n");
749
uint16_t port = (uint16_t) strtol(address+1, NULL, 10);
751
perror("Bad port number");
755
address = connect_to;
756
ret = start_mandos_communication(address, port, if_index, &mc);
978
return ARGP_ERR_UNKNOWN;
983
struct argp argp = { .options = options, .parser = parse_opt,
985
.doc = "Mandos client -- Get and decrypt"
986
" passwords from a Mandos server" };
987
ret = argp_parse(&argp, argc, argv, 0, 0, NULL);
988
if(ret == ARGP_ERR_UNKNOWN){
989
fprintf(stderr, "Unknown error while parsing arguments\n");
990
exitcode = EXIT_FAILURE;
995
/* If the interface is down, bring it up */
996
if(interface[0] != '\0'){
998
/* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
999
messages to mess up the prompt */
1000
ret = klogctl(8, NULL, 5);
1001
bool restore_loglevel = true;
1003
restore_loglevel = false;
764
1008
sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
766
1010
perror("socket");
767
returncode = EXIT_FAILURE;
1011
exitcode = EXIT_FAILURE;
1013
if(restore_loglevel){
1014
ret = klogctl(7, NULL, 0);
770
strcpy(network.ifr_name, interface); /* Spurious warning */
1022
strcpy(network.ifr_name, interface);
771
1023
ret = ioctl(sd, SIOCGIFFLAGS, &network);
774
1025
perror("ioctl SIOCGIFFLAGS");
775
returncode = EXIT_FAILURE;
1027
if(restore_loglevel){
1028
ret = klogctl(7, NULL, 0);
1034
exitcode = EXIT_FAILURE;
778
1037
if((network.ifr_flags & IFF_UP) == 0){
779
1038
network.ifr_flags |= IFF_UP;
780
1039
ret = ioctl(sd, SIOCSIFFLAGS, &network);
782
1041
perror("ioctl SIOCSIFFLAGS");
783
returncode = EXIT_FAILURE;
790
avahi_set_log_function(empty_log);
793
/* Initialize the psuedo-RNG */
794
srand((unsigned int) time(NULL));
796
/* Allocate main loop object */
797
if (!(mc.simple_poll = avahi_simple_poll_new())) {
798
fprintf(stderr, "Failed to create simple poll object.\n");
799
returncode = EXIT_FAILURE;
803
/* Do not publish any local records */
1042
exitcode = EXIT_FAILURE;
1044
if(restore_loglevel){
1045
ret = klogctl(7, NULL, 0);
1054
/* sleep checking until interface is running */
1055
for(int i=0; i < delay * 4; i++){
1056
ret = ioctl(sd, SIOCGIFFLAGS, &network);
1058
perror("ioctl SIOCGIFFLAGS");
1059
} else if(network.ifr_flags & IFF_RUNNING){
1062
struct timespec sleeptime = { .tv_nsec = 250000000 };
1063
ret = nanosleep(&sleeptime, NULL);
1064
if(ret == -1 and errno != EINTR){
1065
perror("nanosleep");
1068
ret = (int)TEMP_FAILURE_RETRY(close(sd));
1073
if(restore_loglevel){
1074
/* Restores kernel loglevel to default */
1075
ret = klogctl(7, NULL, 0);
1097
ret = init_gnutls_global(&mc, pubkey, seckey);
1099
fprintf(stderr, "init_gnutls_global failed\n");
1100
exitcode = EXIT_FAILURE;
1103
gnutls_initialized = true;
1106
if(mkdtemp(tempdir) == NULL){
1110
tempdir_created = true;
1112
if(not init_gpgme(&mc, pubkey, seckey, tempdir)){
1113
fprintf(stderr, "init_gpgme failed\n");
1114
exitcode = EXIT_FAILURE;
1117
gpgme_initialized = true;
1120
if(interface[0] != '\0'){
1121
if_index = (AvahiIfIndex) if_nametoindex(interface);
1123
fprintf(stderr, "No such interface: \"%s\"\n", interface);
1124
exitcode = EXIT_FAILURE;
1129
if(connect_to != NULL){
1130
/* Connect directly, do not use Zeroconf */
1131
/* (Mainly meant for debugging) */
1132
char *address = strrchr(connect_to, ':');
1133
if(address == NULL){
1134
fprintf(stderr, "No colon in address\n");
1135
exitcode = EXIT_FAILURE;
1139
ret = sscanf(address+1, "%" SCNdMAX "%n", &tmpmax, &numchars);
1140
if(ret < 1 or tmpmax != (uint16_t)tmpmax
1141
or address[numchars+1] != '\0'){
1142
fprintf(stderr, "Bad port number\n");
1143
exitcode = EXIT_FAILURE;
1146
port = (uint16_t)tmpmax;
1148
address = connect_to;
1149
/* Colon in address indicates IPv6 */
1151
if(strchr(address, ':') != NULL){
1156
ret = start_mandos_communication(address, port, if_index, &mc,
1159
exitcode = EXIT_FAILURE;
1161
exitcode = EXIT_SUCCESS;
1167
avahi_set_log_function(empty_log);
1170
/* Initialize the pseudo-RNG for Avahi */
1171
srand((unsigned int) time(NULL));
1173
/* Allocate main Avahi loop object */
1174
mc.simple_poll = avahi_simple_poll_new();
1175
if(mc.simple_poll == NULL){
1176
fprintf(stderr, "Avahi: Failed to create simple poll object.\n");
1177
exitcode = EXIT_FAILURE;
1182
AvahiServerConfig config;
1183
/* Do not publish any local Zeroconf records */
804
1184
avahi_server_config_init(&config);
805
1185
config.publish_hinfo = 0;
806
1186
config.publish_addresses = 0;
807
1187
config.publish_workstation = 0;
808
1188
config.publish_domain = 0;
810
1190
/* Allocate a new server */
811
mc.server=avahi_server_new(avahi_simple_poll_get(mc.simple_poll),
812
&config, NULL, NULL, &error);
1191
mc.server = avahi_server_new(avahi_simple_poll_get
1192
(mc.simple_poll), &config, NULL,
814
/* Free the configuration data */
1195
/* Free the Avahi configuration data */
815
1196
avahi_server_config_free(&config);
817
/* Check if creating the server object succeeded */
819
fprintf(stderr, "Failed to create server: %s\n",
820
avahi_strerror(error));
821
returncode = EXIT_FAILURE;
825
/* Create the service browser */
826
sb = avahi_s_service_browser_new(mc.server, if_index,
828
"_mandos._tcp", NULL, 0,
829
browse_callback, &mc);
831
fprintf(stderr, "Failed to create service browser: %s\n",
832
avahi_strerror(avahi_server_errno(mc.server)));
833
returncode = EXIT_FAILURE;
837
/* Run the main loop */
840
fprintf(stderr, "Starting avahi loop search\n");
843
avahi_simple_poll_loop(mc.simple_poll);
848
fprintf(stderr, "%s exiting\n", argv[0]);
853
avahi_s_service_browser_free(sb);
856
avahi_server_free(mc.server);
859
avahi_simple_poll_free(mc.simple_poll);
1199
/* Check if creating the Avahi server object succeeded */
1200
if(mc.server == NULL){
1201
fprintf(stderr, "Failed to create Avahi server: %s\n",
1202
avahi_strerror(error));
1203
exitcode = EXIT_FAILURE;
1207
/* Create the Avahi service browser */
1208
sb = avahi_s_service_browser_new(mc.server, if_index,
1209
AVAHI_PROTO_INET6, "_mandos._tcp",
1210
NULL, 0, browse_callback, &mc);
1212
fprintf(stderr, "Failed to create service browser: %s\n",
1213
avahi_strerror(avahi_server_errno(mc.server)));
1214
exitcode = EXIT_FAILURE;
1218
/* Run the main loop */
1221
fprintf(stderr, "Starting Avahi loop search\n");
1224
avahi_simple_poll_loop(mc.simple_poll);
1229
fprintf(stderr, "%s exiting\n", argv[0]);
1232
/* Cleanup things */
1234
avahi_s_service_browser_free(sb);
1236
if(mc.server != NULL)
1237
avahi_server_free(mc.server);
1239
if(mc.simple_poll != NULL)
1240
avahi_simple_poll_free(mc.simple_poll);
1242
if(gnutls_initialized){
1243
gnutls_certificate_free_credentials(mc.cred);
1244
gnutls_global_deinit();
1245
gnutls_dh_params_deinit(mc.dh_params);
1248
if(gpgme_initialized){
1249
gpgme_release(mc.ctx);
1252
/* Removes the temp directory used by GPGME */
1253
if(tempdir_created){
1255
struct dirent *direntry;
1256
d = opendir(tempdir);
1258
if(errno != ENOENT){
1263
direntry = readdir(d);
1264
if(direntry == NULL){
1267
/* Skip "." and ".." */
1268
if(direntry->d_name[0] == '.'
1269
and (direntry->d_name[1] == '\0'
1270
or (direntry->d_name[1] == '.'
1271
and direntry->d_name[2] == '\0'))){
1274
char *fullname = NULL;
1275
ret = asprintf(&fullname, "%s/%s", tempdir,
1281
ret = remove(fullname);
1283
fprintf(stderr, "remove(\"%s\"): %s\n", fullname,
1290
ret = rmdir(tempdir);
1291
if(ret == -1 and errno != ENOENT){