33
32
#define _LARGEFILE_SOURCE
34
33
#define _FILE_OFFSET_BITS 64
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, 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(),
54
#include <sys/stat.h> /* open() */
55
#include <sys/socket.h> /* socket(), struct sockaddr_in6,
56
struct in6_addr, inet_pton(),
58
#include <fcntl.h> /* open() */
59
#include <dirent.h> /* opendir(), struct dirent, readdir()
61
#include <inttypes.h> /* PRIu16, intmax_t, SCNdMAX */
62
#include <assert.h> /* assert() */
63
#include <errno.h> /* perror(), errno */
64
#include <time.h> /* time() */
39
#include <net/if.h> /* if_nametoindex */
40
#include <sys/ioctl.h> /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
65
42
#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(),
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 */
80
/* All Avahi types, constants and functions
83
45
#include <avahi-core/core.h>
84
46
#include <avahi-core/lookup.h>
85
47
#include <avahi-core/log.h>
87
49
#include <avahi-common/malloc.h>
88
50
#include <avahi-common/error.h>
91
#include <gnutls/gnutls.h> /* All GnuTLS types, constants and
94
init_gnutls_session(),
96
#include <gnutls/openpgp.h>
97
/* gnutls_certificate_set_openpgp_key_file(),
98
GNUTLS_OPENPGP_FMT_BASE64 */
101
#include <gpgme.h> /* All GPGME types, constants and
104
GPGME_PROTOCOL_OpenPGP,
53
#include <sys/types.h> /* socket(), inet_pton() */
54
#include <sys/socket.h> /* socket(), struct sockaddr_in6,
55
struct in6_addr, inet_pton() */
56
#include <gnutls/gnutls.h> /* All GnuTLS stuff */
57
#include <gnutls/openpgp.h> /* GnuTLS with openpgp stuff */
59
#include <unistd.h> /* close() */
60
#include <netinet/in.h>
61
#include <stdbool.h> /* true */
62
#include <string.h> /* memset */
63
#include <arpa/inet.h> /* inet_pton() */
64
#include <iso646.h> /* not */
67
#include <errno.h> /* perror() */
107
73
#define BUFFER_SIZE 256
109
#define PATHDIR "/conf/conf.d/mandos"
110
#define SECKEY "seckey.txt"
111
#define PUBKEY "pubkey.txt"
75
static const char *keydir = "/conf/conf.d/mandos";
76
static const char *pubkeyfile = "pubkey.txt";
77
static const char *seckeyfile = "seckey.txt";
113
79
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>";
118
/* Used for passing in values through the Avahi callback functions */
81
/* Used for passing in values through all the callback functions */
120
83
AvahiSimplePoll *simple_poll;
121
84
AvahiServer *server;
122
85
gnutls_certificate_credentials_t cred;
123
86
unsigned int dh_bits;
124
gnutls_dh_params_t dh_params;
125
87
const char *priority;
91
* Decrypt OpenPGP data using keyrings in HOMEDIR.
94
static ssize_t pgp_packet_decrypt (const char *cryptotext,
98
gpgme_data_t dh_crypto, dh_plain;
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.
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);
141
buffer_capacity += BUFFER_SIZE;
143
return buffer_capacity;
149
static bool init_gpgme(mandos_context *mc, const char *seckey,
150
const char *pubkey, const char *tempdir){
152
100
gpgme_error_t rc;
102
ssize_t plaintext_capacity = 0;
103
ssize_t plaintext_length = 0;
153
104
gpgme_engine_info_t engine_info;
157
* Helper function to insert pub and seckey to the engine keyring.
159
bool import_key(const char *filename){
161
gpgme_data_t pgp_data;
163
fd = (int)TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
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));
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));
183
ret = (int)TEMP_FAILURE_RETRY(close(fd));
187
gpgme_data_release(pgp_data);
192
fprintf(stderr, "Initialize gpgme\n");
107
fprintf(stderr, "Trying to decrypt OpenPGP data\n");
196
111
gpgme_check_version(NULL);
197
112
rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
198
if(rc != GPG_ERR_NO_ERROR){
113
if (rc != GPG_ERR_NO_ERROR){
199
114
fprintf(stderr, "bad gpgme_engine_check_version: %s: %s\n",
200
115
gpgme_strsource(rc), gpgme_strerror(rc));
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){
119
/* Set GPGME home directory for the OpenPGP engine only */
120
rc = gpgme_get_engine_info (&engine_info);
121
if (rc != GPG_ERR_NO_ERROR){
207
122
fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
208
123
gpgme_strsource(rc), gpgme_strerror(rc));
211
126
while(engine_info != NULL){
212
127
if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
213
128
gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
214
engine_info->file_name, tempdir);
129
engine_info->file_name, homedir);
217
132
engine_info = engine_info->next;
219
134
if(engine_info == NULL){
220
fprintf(stderr, "Could not set GPGME home dir to %s\n", tempdir);
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));
232
if(not import_key(pubkey) or not import_key(seckey)){
240
* Decrypt OpenPGP data.
241
* Returns -1 on error
243
static ssize_t pgp_packet_decrypt(const mandos_context *mc,
244
const char *cryptotext,
247
gpgme_data_t dh_crypto, dh_plain;
250
size_t plaintext_capacity = 0;
251
ssize_t plaintext_length = 0;
254
fprintf(stderr, "Trying to decrypt OpenPGP data\n");
135
fprintf(stderr, "Could not set GPGME home dir to %s\n", homedir);
257
139
/* Create new GPGME data buffer from memory cryptotext */
258
140
rc = gpgme_data_new_from_mem(&dh_crypto, cryptotext, crypto_size,
260
if(rc != GPG_ERR_NO_ERROR){
142
if (rc != GPG_ERR_NO_ERROR){
261
143
fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
262
144
gpgme_strsource(rc), gpgme_strerror(rc));
266
148
/* Create new empty GPGME data buffer for the plaintext */
267
149
rc = gpgme_data_new(&dh_plain);
268
if(rc != GPG_ERR_NO_ERROR){
150
if (rc != GPG_ERR_NO_ERROR){
269
151
fprintf(stderr, "bad gpgme_data_new: %s: %s\n",
270
152
gpgme_strsource(rc), gpgme_strerror(rc));
271
153
gpgme_data_release(dh_crypto);
157
/* Create new GPGME "context" */
158
rc = gpgme_new(&ctx);
159
if (rc != GPG_ERR_NO_ERROR){
160
fprintf(stderr, "bad gpgme_new: %s: %s\n",
161
gpgme_strsource(rc), gpgme_strerror(rc));
162
plaintext_length = -1;
275
166
/* Decrypt data from the cryptotext data buffer to the plaintext
277
rc = gpgme_op_decrypt(mc->ctx, dh_crypto, dh_plain);
278
if(rc != GPG_ERR_NO_ERROR){
168
rc = gpgme_op_decrypt(ctx, dh_crypto, dh_plain);
169
if (rc != GPG_ERR_NO_ERROR){
279
170
fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
280
171
gpgme_strsource(rc), gpgme_strerror(rc));
281
172
plaintext_length = -1;
283
gpgme_decrypt_result_t result;
284
result = gpgme_op_decrypt_result(mc->ctx);
286
fprintf(stderr, "gpgme_op_decrypt_result failed\n");
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);
295
gpgme_recipient_t recipient;
296
recipient = result->recipients;
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
305
recipient = recipient->next;
310
173
goto decrypt_end;
314
177
fprintf(stderr, "Decryption of OpenPGP data succeeded\n");
181
gpgme_decrypt_result_t result;
182
result = gpgme_op_decrypt_result(ctx);
184
fprintf(stderr, "gpgme_op_decrypt_result failed\n");
186
fprintf(stderr, "Unsupported algorithm: %s\n",
187
result->unsupported_algorithm);
188
fprintf(stderr, "Wrong key usage: %d\n",
189
result->wrong_key_usage);
190
if(result->file_name != NULL){
191
fprintf(stderr, "File name: %s\n", result->file_name);
193
gpgme_recipient_t recipient;
194
recipient = result->recipients;
196
while(recipient != NULL){
197
fprintf(stderr, "Public key algorithm: %s\n",
198
gpgme_pubkey_algo_name(recipient->pubkey_algo));
199
fprintf(stderr, "Key ID: %s\n", recipient->keyid);
200
fprintf(stderr, "Secret key available: %s\n",
201
recipient->status == GPG_ERR_NO_SECKEY
203
recipient = recipient->next;
317
209
/* Seek back to the beginning of the GPGME plaintext data buffer */
318
if(gpgme_data_seek(dh_plain, (off_t)0, SEEK_SET) == -1){
319
perror("gpgme_data_seek");
210
if (gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET) == -1){
211
perror("pgpme_data_seek");
320
212
plaintext_length = -1;
321
213
goto decrypt_end;
324
216
*plaintext = NULL;
326
plaintext_capacity = adjustbuffer(plaintext,
327
(size_t)plaintext_length,
329
if(plaintext_capacity == 0){
330
perror("adjustbuffer");
218
if (plaintext_length + BUFFER_SIZE > plaintext_capacity){
219
*plaintext = realloc(*plaintext,
220
(unsigned int)plaintext_capacity
222
if (*plaintext == NULL){
331
224
plaintext_length = -1;
332
225
goto decrypt_end;
227
plaintext_capacity += BUFFER_SIZE;
335
230
ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
337
232
/* Print the data, if any */
365
260
return plaintext_length;
368
static const char * safer_gnutls_strerror(int value) {
369
const char *ret = gnutls_strerror(value); /* Spurious warning from
370
-Wunreachable-code */
263
static const char * safer_gnutls_strerror (int value) {
264
const char *ret = gnutls_strerror (value);
372
266
ret = "(unknown)";
376
/* GnuTLS log function callback */
377
270
static void debuggnutls(__attribute__((unused)) int level,
378
271
const char* string){
379
fprintf(stderr, "GnuTLS: %s", string);
272
fprintf(stderr, "%s", string);
382
static int init_gnutls_global(mandos_context *mc,
383
const char *pubkeyfilename,
384
const char *seckeyfilename){
275
static int initgnutls(mandos_context *mc, gnutls_session_t *session,
276
gnutls_dh_params_t *dh_params){
388
281
fprintf(stderr, "Initializing GnuTLS\n");
391
ret = gnutls_global_init();
392
if(ret != GNUTLS_E_SUCCESS) {
393
fprintf(stderr, "GnuTLS global_init: %s\n",
394
safer_gnutls_strerror(ret));
284
if ((ret = gnutls_global_init ())
285
!= GNUTLS_E_SUCCESS) {
286
fprintf (stderr, "global_init: %s\n", safer_gnutls_strerror(ret));
399
/* "Use a log level over 10 to enable all debugging options."
402
291
gnutls_global_set_log_level(11);
403
292
gnutls_global_set_log_function(debuggnutls);
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
413
safer_gnutls_strerror(ret));
414
gnutls_global_deinit();
295
/* openpgp credentials */
296
if ((ret = gnutls_certificate_allocate_credentials (&mc->cred))
297
!= GNUTLS_E_SUCCESS) {
298
fprintf (stderr, "memory error: %s\n",
299
safer_gnutls_strerror(ret));
419
fprintf(stderr, "Attempting to use OpenPGP public key %s and"
420
" secret key %s as GnuTLS credentials\n", pubkeyfilename,
304
fprintf(stderr, "Attempting to use OpenPGP certificate %s"
305
" and keyfile %s as GnuTLS credentials\n", pubkeyfile,
424
309
ret = gnutls_certificate_set_openpgp_key_file
425
(mc->cred, pubkeyfilename, seckeyfilename,
426
GNUTLS_OPENPGP_FMT_BASE64);
427
if(ret != GNUTLS_E_SUCCESS) {
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));
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));
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));
450
gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
456
gnutls_certificate_free_credentials(mc->cred);
457
gnutls_global_deinit();
458
gnutls_dh_params_deinit(mc->dh_params);
462
static int init_gnutls_session(mandos_context *mc,
463
gnutls_session_t *session){
465
/* GnuTLS session creation */
466
ret = gnutls_init(session, GNUTLS_SERVER);
467
if(ret != GNUTLS_E_SUCCESS){
310
(mc->cred, pubkeyfile, seckeyfile, GNUTLS_OPENPGP_FMT_BASE64);
311
if (ret != GNUTLS_E_SUCCESS) {
313
(stderr, "Error[%d] while reading the OpenPGP key pair ('%s',"
315
ret, pubkeyfile, seckeyfile);
316
fprintf(stdout, "The Error is: %s\n",
317
safer_gnutls_strerror(ret));
321
//GnuTLS server initialization
322
if ((ret = gnutls_dh_params_init(dh_params))
323
!= GNUTLS_E_SUCCESS) {
324
fprintf (stderr, "Error in dh parameter initialization: %s\n",
325
safer_gnutls_strerror(ret));
329
if ((ret = gnutls_dh_params_generate2(*dh_params, mc->dh_bits))
330
!= GNUTLS_E_SUCCESS) {
331
fprintf (stderr, "Error in prime generation: %s\n",
332
safer_gnutls_strerror(ret));
336
gnutls_certificate_set_dh_params(mc->cred, *dh_params);
338
// GnuTLS session creation
339
if ((ret = gnutls_init(session, GNUTLS_SERVER))
340
!= GNUTLS_E_SUCCESS){
468
341
fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
469
342
safer_gnutls_strerror(ret));
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);
345
if ((ret = gnutls_priority_set_direct(*session, mc->priority, &err))
346
!= GNUTLS_E_SUCCESS) {
347
fprintf(stderr, "Syntax error at: %s\n", err);
348
fprintf(stderr, "GnuTLS error: %s\n",
349
safer_gnutls_strerror(ret));
484
ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
486
if(ret != GNUTLS_E_SUCCESS) {
487
fprintf(stderr, "Error setting GnuTLS credentials: %s\n",
353
if ((ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
355
!= GNUTLS_E_SUCCESS) {
356
fprintf(stderr, "Error setting a credentials set: %s\n",
488
357
safer_gnutls_strerror(ret));
489
gnutls_deinit(*session);
493
361
/* ignore client certificate if any. */
494
gnutls_certificate_server_set_request(*session,
362
gnutls_certificate_server_set_request (*session,
497
gnutls_dh_set_prime_bits(*session, mc->dh_bits);
365
gnutls_dh_set_prime_bits (*session, mc->dh_bits);
502
/* Avahi log function callback */
503
370
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
504
371
__attribute__((unused)) const char *txt){}
506
/* Called when a Mandos server is found */
507
373
static int start_mandos_communication(const char *ip, uint16_t port,
508
374
AvahiIfIndex if_index,
509
375
mandos_context *mc){
512
union { struct sockaddr in; struct sockaddr_in6 in6; } to;
377
struct sockaddr_in6 to;
513
378
char *buffer = NULL;
514
379
char *decrypted_buffer;
515
380
size_t buffer_length = 0;
516
381
size_t buffer_capacity = 0;
517
382
ssize_t decrypted_buffer_size;
520
385
char interface[IF_NAMESIZE];
521
386
gnutls_session_t session;
523
ret = init_gnutls_session(mc, &session);
387
gnutls_dh_params_t dh_params;
529
fprintf(stderr, "Setting up a tcp connection to %s, port %" PRIu16
390
fprintf(stderr, "Setting up a tcp connection to %s, port %d\n",
533
394
tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
580
ret = connect(tcp_sd, &to.in, sizeof(to));
436
ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
582
438
perror("connect");
586
const char *out = mandos_protocol_version;
589
size_t out_size = strlen(out);
590
ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
591
out_size - written));
597
written += (size_t)ret;
598
if(written < out_size){
601
if(out == mandos_protocol_version){
442
ret = initgnutls (mc, &session, &dh_params);
448
gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t) tcp_sd);
611
451
fprintf(stderr, "Establishing TLS session with %s\n", ip);
614
gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t) tcp_sd);
617
ret = gnutls_handshake(session);
618
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
620
if(ret != GNUTLS_E_SUCCESS){
454
ret = gnutls_handshake (session);
456
if (ret != GNUTLS_E_SUCCESS){
622
fprintf(stderr, "*** GnuTLS Handshake failed ***\n");
458
fprintf(stderr, "\n*** Handshake failed ***\n");
629
/* Read OpenPGP packet that contains the wanted password */
465
//Retrieve OpenPGP packet that contains the wanted password
632
468
fprintf(stderr, "Retrieving pgp encrypted password from %s\n",
637
buffer_capacity = adjustbuffer(&buffer, buffer_length,
639
if(buffer_capacity == 0){
640
perror("adjustbuffer");
473
if (buffer_length + BUFFER_SIZE > buffer_capacity){
474
buffer = realloc(buffer, buffer_capacity + BUFFER_SIZE);
479
buffer_capacity += BUFFER_SIZE;
645
sret = gnutls_record_recv(session, buffer+buffer_length,
482
ret = gnutls_record_recv(session, buffer+buffer_length,
652
489
case GNUTLS_E_INTERRUPTED:
653
490
case GNUTLS_E_AGAIN:
655
492
case GNUTLS_E_REHANDSHAKE:
657
ret = gnutls_handshake(session);
658
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
660
fprintf(stderr, "*** GnuTLS Re-handshake failed ***\n");
493
ret = gnutls_handshake (session);
495
fprintf(stderr, "\n*** Handshake failed ***\n");
667
502
fprintf(stderr, "Unknown error while reading data from"
668
" encrypted session with Mandos server\n");
503
" encrypted session with mandos server\n");
670
gnutls_bye(session, GNUTLS_SHUT_RDWR);
505
gnutls_bye (session, GNUTLS_SHUT_RDWR);
674
buffer_length += (size_t) sret;
509
buffer_length += (size_t) ret;
679
fprintf(stderr, "Closing TLS session\n");
682
gnutls_bye(session, GNUTLS_SHUT_RDWR);
684
if(buffer_length > 0){
685
decrypted_buffer_size = pgp_packet_decrypt(mc, buffer,
513
if (buffer_length > 0){
514
decrypted_buffer_size = pgp_packet_decrypt(buffer,
688
if(decrypted_buffer_size >= 0){
518
if (decrypted_buffer_size >= 0){
690
519
while(written < (size_t) decrypted_buffer_size){
691
ret = (int)fwrite(decrypted_buffer + written, 1,
692
(size_t)decrypted_buffer_size - written,
520
ret = (int)fwrite (decrypted_buffer + written, 1,
521
(size_t)decrypted_buffer_size - written,
694
523
if(ret == 0 and ferror(stdout)){
696
525
fprintf(stderr, "Error writing encrypted data: %s\n",
779
609
void* userdata) {
780
610
mandos_context *mc = userdata;
611
assert(b); /* Spurious warning */
783
613
/* Called whenever a new services becomes available on the LAN or
784
614
is removed from the LAN */
788
618
case AVAHI_BROWSER_FAILURE:
790
fprintf(stderr, "(Avahi browser) %s\n",
620
fprintf(stderr, "(Browser) %s\n",
791
621
avahi_strerror(avahi_server_errno(mc->server)));
792
622
avahi_simple_poll_quit(mc->simple_poll);
795
625
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
626
/* We ignore the returned resolver object. In the callback
627
function we free it. If the server is terminated before
628
the callback function is called the server will free
629
the resolver for us. */
801
if(!(avahi_s_service_resolver_new(mc->server, interface,
631
if (!(avahi_s_service_resolver_new(mc->server, interface,
802
632
protocol, name, type, domain,
803
633
AVAHI_PROTO_INET6, 0,
804
634
resolve_callback, mc)))
805
fprintf(stderr, "Avahi: Failed to resolve service '%s': %s\n",
806
name, avahi_strerror(avahi_server_errno(mc->server)));
635
fprintf(stderr, "Failed to resolve service '%s': %s\n", name,
636
avahi_strerror(avahi_server_errno(mc->server)));
809
639
case AVAHI_BROWSER_REMOVE:
812
642
case AVAHI_BROWSER_ALL_FOR_NOW:
813
643
case AVAHI_BROWSER_CACHE_EXHAUSTED:
815
fprintf(stderr, "No Mandos server found, still searching...\n");
821
int main(int argc, char *argv[]){
648
/* Combines file name and path and returns the malloced new
649
string. some sane checks could/should be added */
650
static const char *combinepath(const char *first, const char *second){
651
size_t f_len = strlen(first);
652
size_t s_len = strlen(second);
653
char *tmp = malloc(f_len + s_len + 2);
658
memcpy(tmp, first, f_len); /* Spurious warning */
662
memcpy(tmp + f_len + 1, second, s_len); /* Spurious warning */
664
tmp[f_len + 1 + s_len] = '\0';
669
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
670
AvahiServerConfig config;
822
671
AvahiSServiceBrowser *sb = NULL;
827
int exitcode = EXIT_SUCCESS;
675
int returncode = EXIT_SUCCESS;
828
676
const char *interface = "eth0";
829
677
struct ifreq network;
833
679
char *connect_to = NULL;
834
char tempdir[] = "/tmp/mandosXXXXXX";
835
bool tempdir_created = false;
836
680
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
837
const char *seckey = PATHDIR "/" SECKEY;
838
const char *pubkey = PATHDIR "/" PUBKEY;
840
681
mandos_context mc = { .simple_poll = NULL, .server = NULL,
841
.dh_bits = 1024, .priority = "SECURE256"
842
":!CTYPE-X.509:+CTYPE-OPENPGP" };
843
bool gnutls_initialized = false;
844
bool gpgme_initialized = false;
847
struct argp_option options[] = {
848
{ .name = "debug", .key = 128,
849
.doc = "Debug mode", .group = 3 },
850
{ .name = "connect", .key = 'c',
851
.arg = "ADDRESS:PORT",
852
.doc = "Connect directly to a specific Mandos server",
854
{ .name = "interface", .key = 'i',
856
.doc = "Interface that will be used to search for Mandos"
859
{ .name = "seckey", .key = 's',
861
.doc = "OpenPGP secret key file base name",
863
{ .name = "pubkey", .key = 'p',
865
.doc = "OpenPGP public key file base name",
867
{ .name = "dh-bits", .key = 129,
869
.doc = "Bit length of the prime number used in the"
870
" Diffie-Hellman key exchange",
872
{ .name = "priority", .key = 130,
874
.doc = "GnuTLS priority string for the TLS handshake",
879
error_t parse_opt(int key, char *arg,
880
struct argp_state *state) {
882
case 128: /* --debug */
885
case 'c': /* --connect */
888
case 'i': /* --interface */
891
case 's': /* --seckey */
894
case 'p': /* --pubkey */
897
case 129: /* --dh-bits */
898
ret = sscanf(arg, "%" SCNdMAX "%n", &tmpmax, &numchars);
899
if(ret < 1 or tmpmax != (typeof(mc.dh_bits))tmpmax
900
or arg[numchars] != '\0'){
901
fprintf(stderr, "Bad number of DH bits\n");
904
mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
906
case 130: /* --priority */
914
return ARGP_ERR_UNKNOWN;
919
struct argp argp = { .options = options, .parser = parse_opt,
921
.doc = "Mandos client -- Get and decrypt"
922
" passwords from a Mandos server" };
923
ret = argp_parse(&argp, argc, argv, 0, 0, NULL);
924
if(ret == ARGP_ERR_UNKNOWN){
925
fprintf(stderr, "Unknown error while parsing arguments\n");
926
exitcode = EXIT_FAILURE;
931
/* If the interface is down, bring it up */
933
sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
936
exitcode = EXIT_FAILURE;
939
strcpy(network.ifr_name, interface);
940
ret = ioctl(sd, SIOCGIFFLAGS, &network);
942
perror("ioctl SIOCGIFFLAGS");
943
exitcode = EXIT_FAILURE;
946
if((network.ifr_flags & IFF_UP) == 0){
947
network.ifr_flags |= IFF_UP;
948
ret = ioctl(sd, SIOCSIFFLAGS, &network);
950
perror("ioctl SIOCSIFFLAGS");
951
exitcode = EXIT_FAILURE;
955
ret = (int)TEMP_FAILURE_RETRY(close(sd));
974
ret = init_gnutls_global(&mc, pubkey, seckey);
976
fprintf(stderr, "init_gnutls_global failed\n");
977
exitcode = EXIT_FAILURE;
980
gnutls_initialized = true;
983
if(mkdtemp(tempdir) == NULL){
987
tempdir_created = true;
989
if(not init_gpgme(&mc, pubkey, seckey, tempdir)){
990
fprintf(stderr, "init_gpgme failed\n");
991
exitcode = EXIT_FAILURE;
994
gpgme_initialized = true;
682
.dh_bits = 1024, .priority = "SECURE256"};
684
debug_int = debug ? 1 : 0;
686
struct option long_options[] = {
687
{"debug", no_argument, &debug_int, 1},
688
{"connect", required_argument, NULL, 'c'},
689
{"interface", required_argument, NULL, 'i'},
690
{"keydir", required_argument, NULL, 'd'},
691
{"seckey", required_argument, NULL, 's'},
692
{"pubkey", required_argument, NULL, 'p'},
693
{"dh-bits", required_argument, NULL, 'D'},
694
{"priority", required_argument, NULL, 'P'},
697
int option_index = 0;
698
ret = getopt_long (argc, argv, "i:", long_options,
725
mc.dh_bits = (unsigned int) strtol(optarg, NULL, 10);
732
mc.priority = optarg;
739
debug = debug_int ? true : false;
741
pubkeyfile = combinepath(keydir, pubkeyfile);
742
if (pubkeyfile == NULL){
743
perror("combinepath");
744
returncode = EXIT_FAILURE;
748
seckeyfile = combinepath(keydir, seckeyfile);
749
if (seckeyfile == NULL){
750
perror("combinepath");
997
754
if_index = (AvahiIfIndex) if_nametoindex(interface);
998
755
if(if_index == 0){
999
756
fprintf(stderr, "No such interface: \"%s\"\n", interface);
1000
exitcode = EXIT_FAILURE;
1004
760
if(connect_to != NULL){
1007
763
char *address = strrchr(connect_to, ':');
1008
764
if(address == NULL){
1009
765
fprintf(stderr, "No colon in address\n");
1010
exitcode = EXIT_FAILURE;
1014
ret = sscanf(address+1, "%" SCNdMAX "%n", &tmpmax, &numchars);
1015
if(ret < 1 or tmpmax != (uint16_t)tmpmax
1016
or address[numchars+1] != '\0'){
1017
fprintf(stderr, "Bad port number\n");
1018
exitcode = EXIT_FAILURE;
1021
port = (uint16_t)tmpmax;
769
uint16_t port = (uint16_t) strtol(address+1, NULL, 10);
771
perror("Bad port number");
1022
774
*address = '\0';
1023
775
address = connect_to;
1024
776
ret = start_mandos_communication(address, port, if_index, &mc);
1026
exitcode = EXIT_FAILURE;
1028
exitcode = EXIT_SUCCESS;
784
sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
787
returncode = EXIT_FAILURE;
790
strcpy(network.ifr_name, interface); /* Spurious warning */
791
ret = ioctl(sd, SIOCGIFFLAGS, &network);
794
perror("ioctl SIOCGIFFLAGS");
795
returncode = EXIT_FAILURE;
798
if((network.ifr_flags & IFF_UP) == 0){
799
network.ifr_flags |= IFF_UP;
800
ret = ioctl(sd, SIOCSIFFLAGS, &network);
802
perror("ioctl SIOCSIFFLAGS");
803
returncode = EXIT_FAILURE;
1034
810
avahi_set_log_function(empty_log);
1037
/* Initialize the pseudo-RNG for Avahi */
813
/* Initialize the psuedo-RNG */
1038
814
srand((unsigned int) time(NULL));
1040
/* Allocate main Avahi loop object */
1041
mc.simple_poll = avahi_simple_poll_new();
1042
if(mc.simple_poll == NULL) {
1043
fprintf(stderr, "Avahi: Failed to create simple poll"
1045
exitcode = EXIT_FAILURE;
1050
AvahiServerConfig config;
1051
/* Do not publish any local Zeroconf records */
1052
avahi_server_config_init(&config);
1053
config.publish_hinfo = 0;
1054
config.publish_addresses = 0;
1055
config.publish_workstation = 0;
1056
config.publish_domain = 0;
1058
/* Allocate a new server */
1059
mc.server = avahi_server_new(avahi_simple_poll_get
1060
(mc.simple_poll), &config, NULL,
1063
/* Free the Avahi configuration data */
1064
avahi_server_config_free(&config);
1067
/* Check if creating the Avahi server object succeeded */
1068
if(mc.server == NULL) {
1069
fprintf(stderr, "Failed to create Avahi server: %s\n",
816
/* Allocate main loop object */
817
if (!(mc.simple_poll = avahi_simple_poll_new())) {
818
fprintf(stderr, "Failed to create simple poll object.\n");
819
returncode = EXIT_FAILURE;
823
/* Do not publish any local records */
824
avahi_server_config_init(&config);
825
config.publish_hinfo = 0;
826
config.publish_addresses = 0;
827
config.publish_workstation = 0;
828
config.publish_domain = 0;
830
/* Allocate a new server */
831
mc.server=avahi_server_new(avahi_simple_poll_get(mc.simple_poll),
832
&config, NULL, NULL, &error);
834
/* Free the configuration data */
835
avahi_server_config_free(&config);
837
/* Check if creating the server object succeeded */
839
fprintf(stderr, "Failed to create server: %s\n",
1070
840
avahi_strerror(error));
1071
exitcode = EXIT_FAILURE;
841
returncode = EXIT_FAILURE;
1075
/* Create the Avahi service browser */
845
/* Create the service browser */
1076
846
sb = avahi_s_service_browser_new(mc.server, if_index,
1077
847
AVAHI_PROTO_INET6,
1078
848
"_mandos._tcp", NULL, 0,
1079
849
browse_callback, &mc);
1081
851
fprintf(stderr, "Failed to create service browser: %s\n",
1082
852
avahi_strerror(avahi_server_errno(mc.server)));
1083
exitcode = EXIT_FAILURE;
853
returncode = EXIT_FAILURE;
1087
857
/* Run the main loop */
1090
fprintf(stderr, "Starting Avahi loop search\n");
860
fprintf(stderr, "Starting avahi loop search\n");
1093
863
avahi_simple_poll_loop(mc.simple_poll);
1098
868
fprintf(stderr, "%s exiting\n", argv[0]);
1101
871
/* Cleanup things */
1103
873
avahi_s_service_browser_free(sb);
1105
if(mc.server != NULL)
1106
876
avahi_server_free(mc.server);
1108
if(mc.simple_poll != NULL)
1109
879
avahi_simple_poll_free(mc.simple_poll);
1111
if(gnutls_initialized){
1112
gnutls_certificate_free_credentials(mc.cred);
1113
gnutls_global_deinit();
1114
gnutls_dh_params_deinit(mc.dh_params);
1117
if(gpgme_initialized){
1118
gpgme_release(mc.ctx);
1121
/* Removes the temp directory used by GPGME */
1122
if(tempdir_created){
1124
struct dirent *direntry;
1125
d = opendir(tempdir);
1127
if(errno != ENOENT){
1132
direntry = readdir(d);
1133
if(direntry == NULL){
1136
/* Skip "." and ".." */
1137
if(direntry->d_name[0] == '.'
1138
and (direntry->d_name[1] == '\0'
1139
or (direntry->d_name[1] == '.'
1140
and direntry->d_name[2] == '\0'))){
1143
char *fullname = NULL;
1144
ret = asprintf(&fullname, "%s/%s", tempdir,
1150
ret = remove(fullname);
1152
fprintf(stderr, "remove(\"%s\"): %s\n", fullname,
1159
ret = rmdir(tempdir);
1160
if(ret == -1 and errno != ENOENT){