93
45
#include <avahi-common/malloc.h>
94
46
#include <avahi-common/error.h>
97
#include <gnutls/gnutls.h> /* All GnuTLS types, constants and
100
init_gnutls_session(),
102
#include <gnutls/openpgp.h>
103
/* gnutls_certificate_set_openpgp_key_file(),
104
GNUTLS_OPENPGP_FMT_BASE64 */
107
#include <gpgme.h> /* All GPGME types, constants and
110
GPGME_PROTOCOL_OpenPGP,
49
#include <sys/types.h> /* socket(), inet_pton() */
50
#include <sys/socket.h> /* socket(), struct sockaddr_in6,
51
struct in6_addr, inet_pton() */
52
#include <gnutls/gnutls.h> /* All GnuTLS stuff */
53
#include <gnutls/openpgp.h> /* GnuTLS with openpgp stuff */
55
#include <unistd.h> /* close() */
56
#include <netinet/in.h>
57
#include <stdbool.h> /* true */
58
#include <string.h> /* memset */
59
#include <arpa/inet.h> /* inet_pton() */
60
#include <iso646.h> /* not */
63
#include <errno.h> /* perror() */
113
69
#define BUFFER_SIZE 256
115
#define PATHDIR "/conf/conf.d/mandos"
116
#define SECKEY "seckey.txt"
117
#define PUBKEY "pubkey.txt"
71
static int dh_bits = 1024;
73
static const char *keydir = "/conf/conf.d/mandos";
74
static const char *pubkeyfile = "pubkey.txt";
75
static const char *seckeyfile = "seckey.txt";
119
77
bool debug = false;
120
static const char mandos_protocol_version[] = "1";
121
const char *argp_program_version = "mandos-client " VERSION;
122
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
124
/* Used for passing in values through the Avahi callback functions */
126
AvahiSimplePoll *simple_poll;
81
gnutls_session_t session;
128
82
gnutls_certificate_credentials_t cred;
129
unsigned int dh_bits;
130
83
gnutls_dh_params_t dh_params;
131
const char *priority;
87
static ssize_t pgp_packet_decrypt (char *packet, size_t packet_size,
90
gpgme_data_t dh_crypto, dh_plain;
135
/* global context so signal handler can reach it*/
139
* Make additional room in "buffer" for at least BUFFER_SIZE
140
* additional bytes. "buffer_capacity" is how much is currently
141
* allocated, "buffer_length" is how much is already used.
143
size_t incbuffer(char **buffer, size_t buffer_length,
144
size_t buffer_capacity){
145
if(buffer_length + BUFFER_SIZE > buffer_capacity){
146
*buffer = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
150
buffer_capacity += BUFFER_SIZE;
152
return buffer_capacity;
158
static bool init_gpgme(const char *seckey,
159
const char *pubkey, const char *tempdir){
94
ssize_t new_packet_capacity = 0;
95
ssize_t new_packet_length = 0;
162
96
gpgme_engine_info_t engine_info;
166
* Helper function to insert pub and seckey to the engine keyring.
168
bool import_key(const char *filename){
170
gpgme_data_t pgp_data;
172
fd = (int)TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
178
rc = gpgme_data_new_from_fd(&pgp_data, fd);
179
if(rc != GPG_ERR_NO_ERROR){
180
fprintf(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
181
gpgme_strsource(rc), gpgme_strerror(rc));
185
rc = gpgme_op_import(mc.ctx, pgp_data);
186
if(rc != GPG_ERR_NO_ERROR){
187
fprintf(stderr, "bad gpgme_op_import: %s: %s\n",
188
gpgme_strsource(rc), gpgme_strerror(rc));
192
ret = (int)TEMP_FAILURE_RETRY(close(fd));
196
gpgme_data_release(pgp_data);
201
fprintf(stderr, "Initializing GPGME\n");
99
fprintf(stderr, "Trying to decrypt OpenPGP packet\n");
205
103
gpgme_check_version(NULL);
206
104
rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
207
if(rc != GPG_ERR_NO_ERROR){
105
if (rc != GPG_ERR_NO_ERROR){
208
106
fprintf(stderr, "bad gpgme_engine_check_version: %s: %s\n",
209
107
gpgme_strsource(rc), gpgme_strerror(rc));
213
/* Set GPGME home directory for the OpenPGP engine only */
214
rc = gpgme_get_engine_info(&engine_info);
215
if(rc != GPG_ERR_NO_ERROR){
111
/* Set GPGME home directory */
112
rc = gpgme_get_engine_info (&engine_info);
113
if (rc != GPG_ERR_NO_ERROR){
216
114
fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
217
115
gpgme_strsource(rc), gpgme_strerror(rc));
220
118
while(engine_info != NULL){
221
119
if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
222
120
gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
223
engine_info->file_name, tempdir);
121
engine_info->file_name, homedir);
226
124
engine_info = engine_info->next;
228
126
if(engine_info == NULL){
229
fprintf(stderr, "Could not set GPGME home dir to %s\n", tempdir);
233
/* Create new GPGME "context" */
234
rc = gpgme_new(&(mc.ctx));
235
if(rc != GPG_ERR_NO_ERROR){
236
fprintf(stderr, "bad gpgme_new: %s: %s\n",
237
gpgme_strsource(rc), gpgme_strerror(rc));
241
if(not import_key(pubkey) or not import_key(seckey)){
249
* Decrypt OpenPGP data.
250
* Returns -1 on error
252
static ssize_t pgp_packet_decrypt(const char *cryptotext,
255
gpgme_data_t dh_crypto, dh_plain;
258
size_t plaintext_capacity = 0;
259
ssize_t plaintext_length = 0;
262
fprintf(stderr, "Trying to decrypt OpenPGP data\n");
265
/* Create new GPGME data buffer from memory cryptotext */
266
rc = gpgme_data_new_from_mem(&dh_crypto, cryptotext, crypto_size,
268
if(rc != GPG_ERR_NO_ERROR){
127
fprintf(stderr, "Could not set home dir to %s\n", homedir);
131
/* Create new GPGME data buffer from packet buffer */
132
rc = gpgme_data_new_from_mem(&dh_crypto, packet, packet_size, 0);
133
if (rc != GPG_ERR_NO_ERROR){
269
134
fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
270
135
gpgme_strsource(rc), gpgme_strerror(rc));
274
139
/* Create new empty GPGME data buffer for the plaintext */
275
140
rc = gpgme_data_new(&dh_plain);
276
if(rc != GPG_ERR_NO_ERROR){
141
if (rc != GPG_ERR_NO_ERROR){
277
142
fprintf(stderr, "bad gpgme_data_new: %s: %s\n",
278
143
gpgme_strsource(rc), gpgme_strerror(rc));
279
gpgme_data_release(dh_crypto);
283
/* Decrypt data from the cryptotext data buffer to the plaintext
285
rc = gpgme_op_decrypt(mc.ctx, dh_crypto, dh_plain);
286
if(rc != GPG_ERR_NO_ERROR){
147
/* Create new GPGME "context" */
148
rc = gpgme_new(&ctx);
149
if (rc != GPG_ERR_NO_ERROR){
150
fprintf(stderr, "bad gpgme_new: %s: %s\n",
151
gpgme_strsource(rc), gpgme_strerror(rc));
155
/* Decrypt data from the FILE pointer to the plaintext data
157
rc = gpgme_op_decrypt(ctx, dh_crypto, dh_plain);
158
if (rc != GPG_ERR_NO_ERROR){
287
159
fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
288
160
gpgme_strsource(rc), gpgme_strerror(rc));
289
plaintext_length = -1;
291
gpgme_decrypt_result_t result;
292
result = gpgme_op_decrypt_result(mc.ctx);
294
fprintf(stderr, "gpgme_op_decrypt_result failed\n");
296
fprintf(stderr, "Unsupported algorithm: %s\n",
297
result->unsupported_algorithm);
298
fprintf(stderr, "Wrong key usage: %u\n",
299
result->wrong_key_usage);
300
if(result->file_name != NULL){
301
fprintf(stderr, "File name: %s\n", result->file_name);
303
gpgme_recipient_t recipient;
304
recipient = result->recipients;
306
while(recipient != NULL){
307
fprintf(stderr, "Public key algorithm: %s\n",
308
gpgme_pubkey_algo_name(recipient->pubkey_algo));
309
fprintf(stderr, "Key ID: %s\n", recipient->keyid);
310
fprintf(stderr, "Secret key available: %s\n",
311
recipient->status == GPG_ERR_NO_SECKEY
313
recipient = recipient->next;
165
fprintf(stderr, "Decryption of OpenPGP packet succeeded\n");
169
gpgme_decrypt_result_t result;
170
result = gpgme_op_decrypt_result(ctx);
172
fprintf(stderr, "gpgme_op_decrypt_result failed\n");
174
fprintf(stderr, "Unsupported algorithm: %s\n",
175
result->unsupported_algorithm);
176
fprintf(stderr, "Wrong key usage: %d\n",
177
result->wrong_key_usage);
178
if(result->file_name != NULL){
179
fprintf(stderr, "File name: %s\n", result->file_name);
181
gpgme_recipient_t recipient;
182
recipient = result->recipients;
184
while(recipient != NULL){
185
fprintf(stderr, "Public key algorithm: %s\n",
186
gpgme_pubkey_algo_name(recipient->pubkey_algo));
187
fprintf(stderr, "Key ID: %s\n", recipient->keyid);
188
fprintf(stderr, "Secret key available: %s\n",
189
recipient->status == GPG_ERR_NO_SECKEY
191
recipient = recipient->next;
322
fprintf(stderr, "Decryption of OpenPGP data succeeded\n");
197
/* Delete the GPGME FILE pointer cryptotext data buffer */
198
gpgme_data_release(dh_crypto);
325
200
/* Seek back to the beginning of the GPGME plaintext data buffer */
326
if(gpgme_data_seek(dh_plain, (off_t)0, SEEK_SET) == -1){
327
perror("gpgme_data_seek");
328
plaintext_length = -1;
201
if (gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET) == -1){
202
perror("pgpme_data_seek");
334
plaintext_capacity = incbuffer(plaintext,
335
(size_t)plaintext_length,
337
if(plaintext_capacity == 0){
339
plaintext_length = -1;
207
if (new_packet_length + BUFFER_SIZE > new_packet_capacity){
208
*new_packet = realloc(*new_packet,
209
(unsigned int)new_packet_capacity
211
if (*new_packet == NULL){
215
new_packet_capacity += BUFFER_SIZE;
343
ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
218
ret = gpgme_data_read(dh_plain, *new_packet + new_packet_length,
345
220
/* Print the data, if any */
351
225
perror("gpgme_data_read");
352
plaintext_length = -1;
355
plaintext_length += ret;
359
fprintf(stderr, "Decrypted password is: ");
360
for(ssize_t i = 0; i < plaintext_length; i++){
361
fprintf(stderr, "%02hhX ", (*plaintext)[i]);
363
fprintf(stderr, "\n");
368
/* Delete the GPGME cryptotext data buffer */
369
gpgme_data_release(dh_crypto);
228
new_packet_length += ret;
231
/* FIXME: check characters before printing to screen so to not print
232
terminal control characters */
234
/* fprintf(stderr, "decrypted password is: "); */
235
/* fwrite(*new_packet, 1, new_packet_length, stderr); */
236
/* fprintf(stderr, "\n"); */
371
239
/* Delete the GPGME plaintext data buffer */
372
240
gpgme_data_release(dh_plain);
373
return plaintext_length;
241
return new_packet_length;
376
static const char * safer_gnutls_strerror(int value){
377
const char *ret = gnutls_strerror(value); /* Spurious warning from
378
-Wunreachable-code */
244
static const char * safer_gnutls_strerror (int value) {
245
const char *ret = gnutls_strerror (value);
380
247
ret = "(unknown)";
384
/* GnuTLS log function callback */
385
251
static void debuggnutls(__attribute__((unused)) int level,
386
252
const char* string){
387
fprintf(stderr, "GnuTLS: %s", string);
253
fprintf(stderr, "%s", string);
390
static int init_gnutls_global(const char *pubkeyfilename,
391
const char *seckeyfilename){
256
static int initgnutls(encrypted_session *es){
395
261
fprintf(stderr, "Initializing GnuTLS\n");
398
ret = gnutls_global_init();
399
if(ret != GNUTLS_E_SUCCESS){
400
fprintf(stderr, "GnuTLS global_init: %s\n",
401
safer_gnutls_strerror(ret));
264
if ((ret = gnutls_global_init ())
265
!= GNUTLS_E_SUCCESS) {
266
fprintf (stderr, "global_init: %s\n", safer_gnutls_strerror(ret));
406
/* "Use a log level over 10 to enable all debugging options."
409
271
gnutls_global_set_log_level(11);
410
272
gnutls_global_set_log_function(debuggnutls);
413
/* OpenPGP credentials */
414
gnutls_certificate_allocate_credentials(&mc.cred);
415
if(ret != GNUTLS_E_SUCCESS){
416
fprintf(stderr, "GnuTLS memory error: %s\n", /* Spurious warning
420
safer_gnutls_strerror(ret));
421
gnutls_global_deinit();
275
/* openpgp credentials */
276
if ((ret = gnutls_certificate_allocate_credentials (&es->cred))
277
!= GNUTLS_E_SUCCESS) {
278
fprintf (stderr, "memory error: %s\n",
279
safer_gnutls_strerror(ret));
426
fprintf(stderr, "Attempting to use OpenPGP public key %s and"
427
" secret key %s as GnuTLS credentials\n", pubkeyfilename,
284
fprintf(stderr, "Attempting to use OpenPGP certificate %s"
285
" and keyfile %s as GnuTLS credentials\n", pubkeyfile,
431
289
ret = gnutls_certificate_set_openpgp_key_file
432
(mc.cred, pubkeyfilename, seckeyfilename,
433
GNUTLS_OPENPGP_FMT_BASE64);
434
if(ret != GNUTLS_E_SUCCESS){
436
"Error[%d] while reading the OpenPGP key pair ('%s',"
437
" '%s')\n", ret, pubkeyfilename, seckeyfilename);
438
fprintf(stderr, "The GnuTLS error is: %s\n",
439
safer_gnutls_strerror(ret));
443
/* GnuTLS server initialization */
444
ret = gnutls_dh_params_init(&mc.dh_params);
445
if(ret != GNUTLS_E_SUCCESS){
446
fprintf(stderr, "Error in GnuTLS DH parameter initialization:"
447
" %s\n", safer_gnutls_strerror(ret));
450
ret = gnutls_dh_params_generate2(mc.dh_params, mc.dh_bits);
451
if(ret != GNUTLS_E_SUCCESS){
452
fprintf(stderr, "Error in GnuTLS prime generation: %s\n",
453
safer_gnutls_strerror(ret));
457
gnutls_certificate_set_dh_params(mc.cred, mc.dh_params);
463
gnutls_certificate_free_credentials(mc.cred);
464
gnutls_global_deinit();
465
gnutls_dh_params_deinit(mc.dh_params);
469
static int init_gnutls_session(gnutls_session_t *session){
471
/* GnuTLS session creation */
472
ret = gnutls_init(session, GNUTLS_SERVER);
473
if(ret != GNUTLS_E_SUCCESS){
290
(es->cred, pubkeyfile, seckeyfile, GNUTLS_OPENPGP_FMT_BASE64);
291
if (ret != GNUTLS_E_SUCCESS) {
293
(stderr, "Error[%d] while reading the OpenPGP key pair ('%s',"
295
ret, pubkeyfile, seckeyfile);
296
fprintf(stdout, "The Error is: %s\n",
297
safer_gnutls_strerror(ret));
301
//GnuTLS server initialization
302
if ((ret = gnutls_dh_params_init (&es->dh_params))
303
!= GNUTLS_E_SUCCESS) {
304
fprintf (stderr, "Error in dh parameter initialization: %s\n",
305
safer_gnutls_strerror(ret));
309
if ((ret = gnutls_dh_params_generate2 (es->dh_params, dh_bits))
310
!= GNUTLS_E_SUCCESS) {
311
fprintf (stderr, "Error in prime generation: %s\n",
312
safer_gnutls_strerror(ret));
316
gnutls_certificate_set_dh_params (es->cred, es->dh_params);
318
// GnuTLS session creation
319
if ((ret = gnutls_init (&es->session, GNUTLS_SERVER))
320
!= GNUTLS_E_SUCCESS){
474
321
fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
475
322
safer_gnutls_strerror(ret));
480
ret = gnutls_priority_set_direct(*session, mc.priority, &err);
481
if(ret != GNUTLS_E_SUCCESS){
482
fprintf(stderr, "Syntax error at: %s\n", err);
483
fprintf(stderr, "GnuTLS error: %s\n",
484
safer_gnutls_strerror(ret));
485
gnutls_deinit(*session);
325
if ((ret = gnutls_priority_set_direct (es->session, "NORMAL", &err))
326
!= GNUTLS_E_SUCCESS) {
327
fprintf(stderr, "Syntax error at: %s\n", err);
328
fprintf(stderr, "GnuTLS error: %s\n",
329
safer_gnutls_strerror(ret));
490
ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
492
if(ret != GNUTLS_E_SUCCESS){
493
fprintf(stderr, "Error setting GnuTLS credentials: %s\n",
333
if ((ret = gnutls_credentials_set
334
(es->session, GNUTLS_CRD_CERTIFICATE, es->cred))
335
!= GNUTLS_E_SUCCESS) {
336
fprintf(stderr, "Error setting a credentials set: %s\n",
494
337
safer_gnutls_strerror(ret));
495
gnutls_deinit(*session);
499
341
/* ignore client certificate if any. */
500
gnutls_certificate_server_set_request(*session,
342
gnutls_certificate_server_set_request (es->session,
503
gnutls_dh_set_prime_bits(*session, mc.dh_bits);
345
gnutls_dh_set_prime_bits (es->session, dh_bits);
508
/* Avahi log function callback */
509
350
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
510
351
__attribute__((unused)) const char *txt){}
512
/* Called when a Mandos server is found */
513
353
static int start_mandos_communication(const char *ip, uint16_t port,
514
AvahiIfIndex if_index,
354
AvahiIfIndex if_index){
519
struct sockaddr_in in;
520
struct sockaddr_in6 in6;
356
struct sockaddr_in6 to;
357
encrypted_session es;
522
358
char *buffer = NULL;
523
359
char *decrypted_buffer;
524
360
size_t buffer_length = 0;
525
361
size_t buffer_capacity = 0;
526
362
ssize_t decrypted_buffer_size;
529
gnutls_session_t session;
530
int pf; /* Protocol family */
540
fprintf(stderr, "Bad address family: %d\n", af);
544
ret = init_gnutls_session(&session);
365
char interface[IF_NAMESIZE];
550
fprintf(stderr, "Setting up a TCP connection to %s, port %" PRIu16
368
fprintf(stderr, "Setting up a tcp connection to %s, port %d\n",
554
tcp_sd = socket(pf, SOCK_STREAM, 0);
372
tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
556
374
perror("socket");
560
memset(&to, 0, sizeof(to));
562
to.in6.sin6_family = (uint16_t)af;
563
ret = inet_pton(af, ip, &to.in6.sin6_addr);
565
to.in.sin_family = (sa_family_t)af;
566
ret = inet_pton(af, ip, &to.in.sin_addr);
378
if(if_indextoname((unsigned int)if_index, interface) == NULL){
380
perror("if_indextoname");
386
fprintf(stderr, "Binding to interface %s\n", interface);
389
memset(&to,0,sizeof(to)); /* Spurious warning */
390
to.sin6_family = AF_INET6;
391
ret = inet_pton(AF_INET6, ip, &to.sin6_addr);
569
393
perror("inet_pton");
573
397
fprintf(stderr, "Bad address: %s\n", ip);
577
to.in6.sin6_port = htons(port); /* Spurious warnings from
579
-Wunreachable-code */
581
if(IN6_IS_ADDR_LINKLOCAL /* Spurious warnings from */
582
(&to.in6.sin6_addr)){ /* -Wstrict-aliasing=2 or lower and
584
if(if_index == AVAHI_IF_UNSPEC){
585
fprintf(stderr, "An IPv6 link-local address is incomplete"
586
" without a network interface\n");
589
/* Set the network interface number as scope */
590
to.in6.sin6_scope_id = (uint32_t)if_index;
593
to.in.sin_port = htons(port); /* Spurious warnings from
595
-Wunreachable-code */
400
to.sin6_port = htons(port); /* Spurious warning */
402
to.sin6_scope_id = (uint32_t)if_index;
599
if(af == AF_INET6 and if_index != AVAHI_IF_UNSPEC){
600
char interface[IF_NAMESIZE];
601
if(if_indextoname((unsigned int)if_index, interface) == NULL){
602
perror("if_indextoname");
604
fprintf(stderr, "Connection to: %s%%%s, port %" PRIu16 "\n",
605
ip, interface, port);
608
fprintf(stderr, "Connection to: %s, port %" PRIu16 "\n", ip,
611
char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ?
612
INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = "";
615
pcret = inet_ntop(af, &(to.in6.sin6_addr), addrstr,
618
pcret = inet_ntop(af, &(to.in.sin_addr), addrstr,
405
fprintf(stderr, "Connection to: %s, port %d\n", ip, port);
406
char addrstr[INET6_ADDRSTRLEN] = "";
407
if(inet_ntop(to.sin6_family, &(to.sin6_addr), addrstr,
408
sizeof(addrstr)) == NULL){
622
409
perror("inet_ntop");
624
411
if(strcmp(addrstr, ip) != 0){
625
fprintf(stderr, "Canonical address form: %s\n", addrstr);
412
fprintf(stderr, "Canonical address form: %s\n",
413
addrstr, ntohs(to.sin6_port));
631
ret = connect(tcp_sd, &to.in6, sizeof(to));
633
ret = connect(tcp_sd, &to.in, sizeof(to)); /* IPv4 */
418
ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
636
420
perror("connect");
640
const char *out = mandos_protocol_version;
643
size_t out_size = strlen(out);
644
ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
645
out_size - written));
651
written += (size_t)ret;
652
if(written < out_size){
655
if(out == mandos_protocol_version){
424
ret = initgnutls (&es);
430
gnutls_transport_set_ptr (es.session,
431
(gnutls_transport_ptr_t) tcp_sd);
665
434
fprintf(stderr, "Establishing TLS session with %s\n", ip);
668
gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t) tcp_sd);
671
ret = gnutls_handshake(session);
672
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
674
if(ret != GNUTLS_E_SUCCESS){
437
ret = gnutls_handshake (es.session);
439
if (ret != GNUTLS_E_SUCCESS){
676
fprintf(stderr, "*** GnuTLS Handshake failed ***\n");
441
fprintf(stderr, "\n*** Handshake failed ***\n");
683
/* Read OpenPGP packet that contains the wanted password */
448
//Retrieve OpenPGP packet that contains the wanted password
686
fprintf(stderr, "Retrieving OpenPGP encrypted password from %s\n",
451
fprintf(stderr, "Retrieving pgp encrypted password from %s\n",
691
buffer_capacity = incbuffer(&buffer, buffer_length,
693
if(buffer_capacity == 0){
456
if (buffer_length + BUFFER_SIZE > buffer_capacity){
457
buffer = realloc(buffer, buffer_capacity + BUFFER_SIZE);
462
buffer_capacity += BUFFER_SIZE;
699
sret = gnutls_record_recv(session, buffer+buffer_length,
465
ret = gnutls_record_recv
466
(es.session, buffer+buffer_length, BUFFER_SIZE);
706
472
case GNUTLS_E_INTERRUPTED:
707
473
case GNUTLS_E_AGAIN:
709
475
case GNUTLS_E_REHANDSHAKE:
711
ret = gnutls_handshake(session);
712
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
714
fprintf(stderr, "*** GnuTLS Re-handshake failed ***\n");
476
ret = gnutls_handshake (es.session);
478
fprintf(stderr, "\n*** Handshake failed ***\n");
721
485
fprintf(stderr, "Unknown error while reading data from"
722
" encrypted session with Mandos server\n");
486
" encrypted session with mandos server\n");
724
gnutls_bye(session, GNUTLS_SHUT_RDWR);
488
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
728
buffer_length += (size_t) sret;
492
buffer_length += (size_t) ret;
733
fprintf(stderr, "Closing TLS session\n");
736
gnutls_bye(session, GNUTLS_SHUT_RDWR);
738
if(buffer_length > 0){
496
if (buffer_length > 0){
739
497
decrypted_buffer_size = pgp_packet_decrypt(buffer,
742
if(decrypted_buffer_size >= 0){
501
if (decrypted_buffer_size >= 0){
744
502
while(written < (size_t) decrypted_buffer_size){
745
ret = (int)fwrite(decrypted_buffer + written, 1,
746
(size_t)decrypted_buffer_size - written,
503
ret = (int)fwrite (decrypted_buffer + written, 1,
504
(size_t)decrypted_buffer_size - written,
748
506
if(ret == 0 and ferror(stdout)){
750
508
fprintf(stderr, "Error writing encrypted data: %s\n",
807
571
char ip[AVAHI_ADDRESS_STR_MAX];
808
572
avahi_address_snprint(ip, sizeof(ip), address);
810
fprintf(stderr, "Mandos server \"%s\" found on %s (%s, %"
811
PRIdMAX ") on port %" PRIu16 "\n", name, host_name,
812
ip, (intmax_t)interface, port);
574
fprintf(stderr, "Mandos server \"%s\" found on %s (%s) on"
575
" port %d\n", name, host_name, ip, port);
814
int ret = start_mandos_communication(ip, port, interface,
815
avahi_proto_to_af(proto));
817
avahi_simple_poll_quit(mc.simple_poll);
577
int ret = start_mandos_communication(ip, port, interface);
821
583
avahi_s_service_resolver_free(r);
824
static void browse_callback(AvahiSServiceBrowser *b,
825
AvahiIfIndex interface,
826
AvahiProtocol protocol,
827
AvahiBrowserEvent event,
831
AVAHI_GCC_UNUSED AvahiLookupResultFlags
833
AVAHI_GCC_UNUSED void* userdata){
836
/* Called whenever a new services becomes available on the LAN or
837
is removed from the LAN */
841
case AVAHI_BROWSER_FAILURE:
843
fprintf(stderr, "(Avahi browser) %s\n",
844
avahi_strerror(avahi_server_errno(mc.server)));
845
avahi_simple_poll_quit(mc.simple_poll);
848
case AVAHI_BROWSER_NEW:
849
/* We ignore the returned Avahi resolver object. In the callback
850
function we free it. If the Avahi server is terminated before
851
the callback function is called the Avahi server will free the
854
if(!(avahi_s_service_resolver_new(mc.server, interface,
855
protocol, name, type, domain,
856
AVAHI_PROTO_INET6, 0,
857
resolve_callback, NULL)))
858
fprintf(stderr, "Avahi: Failed to resolve service '%s': %s\n",
859
name, avahi_strerror(avahi_server_errno(mc.server)));
862
case AVAHI_BROWSER_REMOVE:
865
case AVAHI_BROWSER_ALL_FOR_NOW:
866
case AVAHI_BROWSER_CACHE_EXHAUSTED:
868
fprintf(stderr, "No Mandos server found, still searching...\n");
874
static void handle_sigterm(__attribute__((unused)) int sig){
875
int old_errno = errno;
876
avahi_simple_poll_quit(mc.simple_poll);
880
int main(int argc, char *argv[]){
881
AvahiSServiceBrowser *sb = NULL;
886
int exitcode = EXIT_SUCCESS;
887
const char *interface = "eth0";
888
struct ifreq network;
892
char *connect_to = NULL;
893
char tempdir[] = "/tmp/mandosXXXXXX";
894
bool tempdir_created = false;
895
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
896
const char *seckey = PATHDIR "/" SECKEY;
897
const char *pubkey = PATHDIR "/" PUBKEY;
899
/* Initialize Mandos context */
900
mc = (mandos_context){ .simple_poll = NULL, .server = NULL,
901
.dh_bits = 1024, .priority = "SECURE256"
902
":!CTYPE-X.509:+CTYPE-OPENPGP" };
903
bool gnutls_initialized = false;
904
bool gpgme_initialized = false;
907
struct sigaction old_sigterm_action;
908
struct sigaction sigterm_action = { .sa_handler = handle_sigterm };
911
struct argp_option options[] = {
912
{ .name = "debug", .key = 128,
913
.doc = "Debug mode", .group = 3 },
914
{ .name = "connect", .key = 'c',
915
.arg = "ADDRESS:PORT",
916
.doc = "Connect directly to a specific Mandos server",
918
{ .name = "interface", .key = 'i',
920
.doc = "Network interface that will be used to search for"
923
{ .name = "seckey", .key = 's',
925
.doc = "OpenPGP secret key file base name",
927
{ .name = "pubkey", .key = 'p',
929
.doc = "OpenPGP public key file base name",
931
{ .name = "dh-bits", .key = 129,
933
.doc = "Bit length of the prime number used in the"
934
" Diffie-Hellman key exchange",
936
{ .name = "priority", .key = 130,
938
.doc = "GnuTLS priority string for the TLS handshake",
940
{ .name = "delay", .key = 131,
942
.doc = "Maximum delay to wait for interface startup",
947
error_t parse_opt(int key, char *arg,
948
struct argp_state *state){
950
case 128: /* --debug */
953
case 'c': /* --connect */
956
case 'i': /* --interface */
959
case 's': /* --seckey */
962
case 'p': /* --pubkey */
965
case 129: /* --dh-bits */
966
ret = sscanf(arg, "%" SCNdMAX "%n", &tmpmax, &numchars);
967
if(ret < 1 or tmpmax != (typeof(mc.dh_bits))tmpmax
968
or arg[numchars] != '\0'){
969
fprintf(stderr, "Bad number of DH bits\n");
972
mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
974
case 130: /* --priority */
977
case 131: /* --delay */
978
ret = sscanf(arg, "%lf%n", &delay, &numchars);
979
if(ret < 1 or arg[numchars] != '\0'){
980
fprintf(stderr, "Bad delay\n");
989
return ARGP_ERR_UNKNOWN;
994
struct argp argp = { .options = options, .parser = parse_opt,
996
.doc = "Mandos client -- Get and decrypt"
997
" passwords from a Mandos server" };
998
ret = argp_parse(&argp, argc, argv, 0, 0, NULL);
999
if(ret == ARGP_ERR_UNKNOWN){
1000
fprintf(stderr, "Unknown error while parsing arguments\n");
1001
exitcode = EXIT_FAILURE;
1006
/* If the interface is down, bring it up */
1007
if(interface[0] != '\0'){
1009
/* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
1010
messages to mess up the prompt */
1011
ret = klogctl(8, NULL, 5);
1012
bool restore_loglevel = true;
1014
restore_loglevel = false;
1019
sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1022
exitcode = EXIT_FAILURE;
1024
if(restore_loglevel){
1025
ret = klogctl(7, NULL, 0);
1033
strcpy(network.ifr_name, interface);
1034
ret = ioctl(sd, SIOCGIFFLAGS, &network);
1036
perror("ioctl SIOCGIFFLAGS");
1038
if(restore_loglevel){
1039
ret = klogctl(7, NULL, 0);
1045
exitcode = EXIT_FAILURE;
1048
if((network.ifr_flags & IFF_UP) == 0){
1049
network.ifr_flags |= IFF_UP;
1050
ret = ioctl(sd, SIOCSIFFLAGS, &network);
1052
perror("ioctl SIOCSIFFLAGS");
1053
exitcode = EXIT_FAILURE;
1055
if(restore_loglevel){
1056
ret = klogctl(7, NULL, 0);
1065
/* sleep checking until interface is running */
1066
for(int i=0; i < delay * 4; i++){
1067
ret = ioctl(sd, SIOCGIFFLAGS, &network);
1069
perror("ioctl SIOCGIFFLAGS");
1070
} else if(network.ifr_flags & IFF_RUNNING){
1073
struct timespec sleeptime = { .tv_nsec = 250000000 };
1074
ret = nanosleep(&sleeptime, NULL);
1075
if(ret == -1 and errno != EINTR){
1076
perror("nanosleep");
1079
ret = (int)TEMP_FAILURE_RETRY(close(sd));
1084
if(restore_loglevel){
1085
/* Restores kernel loglevel to default */
1086
ret = klogctl(7, NULL, 0);
1108
ret = init_gnutls_global(pubkey, seckey);
1110
fprintf(stderr, "init_gnutls_global failed\n");
1111
exitcode = EXIT_FAILURE;
1114
gnutls_initialized = true;
1117
if(mkdtemp(tempdir) == NULL){
1121
tempdir_created = true;
1123
if(not init_gpgme(pubkey, seckey, tempdir)){
1124
fprintf(stderr, "init_gpgme failed\n");
1125
exitcode = EXIT_FAILURE;
1128
gpgme_initialized = true;
1131
if(interface[0] != '\0'){
1132
if_index = (AvahiIfIndex) if_nametoindex(interface);
1134
fprintf(stderr, "No such interface: \"%s\"\n", interface);
1135
exitcode = EXIT_FAILURE;
1140
if(connect_to != NULL){
1141
/* Connect directly, do not use Zeroconf */
1142
/* (Mainly meant for debugging) */
1143
char *address = strrchr(connect_to, ':');
1144
if(address == NULL){
1145
fprintf(stderr, "No colon in address\n");
1146
exitcode = EXIT_FAILURE;
1150
ret = sscanf(address+1, "%" SCNdMAX "%n", &tmpmax, &numchars);
1151
if(ret < 1 or tmpmax != (uint16_t)tmpmax
1152
or address[numchars+1] != '\0'){
1153
fprintf(stderr, "Bad port number\n");
1154
exitcode = EXIT_FAILURE;
1157
port = (uint16_t)tmpmax;
1159
address = connect_to;
1160
/* Colon in address indicates IPv6 */
1162
if(strchr(address, ':') != NULL){
1167
ret = start_mandos_communication(address, port, if_index, af);
1169
exitcode = EXIT_FAILURE;
1171
exitcode = EXIT_SUCCESS;
1177
avahi_set_log_function(empty_log);
1180
/* Initialize the pseudo-RNG for Avahi */
1181
srand((unsigned int) time(NULL));
1183
/* Allocate main Avahi loop object */
1184
mc.simple_poll = avahi_simple_poll_new();
1185
if(mc.simple_poll == NULL){
1186
fprintf(stderr, "Avahi: Failed to create simple poll object.\n");
1187
exitcode = EXIT_FAILURE;
586
static void browse_callback(
587
AvahiSServiceBrowser *b,
588
AvahiIfIndex interface,
589
AvahiProtocol protocol,
590
AvahiBrowserEvent event,
594
AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
597
AvahiServer *s = userdata;
598
assert(b); /* Spurious warning */
600
/* Called whenever a new services becomes available on the LAN or
601
is removed from the LAN */
605
case AVAHI_BROWSER_FAILURE:
607
fprintf(stderr, "(Browser) %s\n",
608
avahi_strerror(avahi_server_errno(server)));
609
avahi_simple_poll_quit(simple_poll);
612
case AVAHI_BROWSER_NEW:
613
/* We ignore the returned resolver object. In the callback
614
function we free it. If the server is terminated before
615
the callback function is called the server will free
616
the resolver for us. */
618
if (!(avahi_s_service_resolver_new(s, interface, protocol, name,
620
AVAHI_PROTO_INET6, 0,
621
resolve_callback, s)))
622
fprintf(stderr, "Failed to resolve service '%s': %s\n", name,
623
avahi_strerror(avahi_server_errno(s)));
626
case AVAHI_BROWSER_REMOVE:
629
case AVAHI_BROWSER_ALL_FOR_NOW:
630
case AVAHI_BROWSER_CACHE_EXHAUSTED:
635
/* Combines file name and path and returns the malloced new
636
string. some sane checks could/should be added */
637
static const char *combinepath(const char *first, const char *second){
638
size_t f_len = strlen(first);
639
size_t s_len = strlen(second);
640
char *tmp = malloc(f_len + s_len + 2);
645
memcpy(tmp, first, f_len);
649
memcpy(tmp + f_len + 1, second, s_len);
651
tmp[f_len + 1 + s_len] = '\0';
656
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
1192
657
AvahiServerConfig config;
1193
/* Do not publish any local Zeroconf records */
658
AvahiSServiceBrowser *sb = NULL;
662
int returncode = EXIT_SUCCESS;
663
const char *interface = NULL;
664
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
665
char *connect_to = NULL;
667
debug_int = debug ? 1 : 0;
669
static struct option long_options[] = {
670
{"debug", no_argument, &debug_int, 1},
671
{"connect", required_argument, NULL, 'C'},
672
{"interface", required_argument, NULL, 'i'},
673
{"keydir", required_argument, NULL, 'd'},
674
{"seckey", required_argument, NULL, 'c'},
675
{"pubkey", required_argument, NULL, 'k'},
676
{"dh-bits", required_argument, NULL, 'D'},
679
int option_index = 0;
680
ret = getopt_long (argc, argv, "i:", long_options,
706
dh_bits = atoi(optarg);
714
debug = debug_int ? true : false;
716
pubkeyfile = combinepath(keydir, pubkeyfile);
717
if (pubkeyfile == NULL){
718
perror("combinepath");
722
if(interface != NULL){
723
if_index = (AvahiIfIndex) if_nametoindex(interface);
725
fprintf(stderr, "No such interface: \"%s\"\n", interface);
730
if(connect_to != NULL){
731
/* Connect directly, do not use Zeroconf */
732
/* (Mainly meant for debugging) */
733
char *address = strrchr(connect_to, ':');
735
fprintf(stderr, "No colon in address\n");
739
uint16_t port = (uint16_t) strtol(address+1, NULL, 10);
741
perror("Bad port number");
745
address = connect_to;
746
ret = start_mandos_communication(address, port, if_index);
754
seckeyfile = combinepath(keydir, seckeyfile);
755
if (seckeyfile == NULL){
756
perror("combinepath");
761
avahi_set_log_function(empty_log);
764
/* Initialize the psuedo-RNG */
765
srand((unsigned int) time(NULL));
767
/* Allocate main loop object */
768
if (!(simple_poll = avahi_simple_poll_new())) {
769
fprintf(stderr, "Failed to create simple poll object.\n");
774
/* Do not publish any local records */
1194
775
avahi_server_config_init(&config);
1195
776
config.publish_hinfo = 0;
1196
777
config.publish_addresses = 0;
1197
778
config.publish_workstation = 0;
1198
779
config.publish_domain = 0;
1200
781
/* Allocate a new server */
1201
mc.server = avahi_server_new(avahi_simple_poll_get
1202
(mc.simple_poll), &config, NULL,
1205
/* Free the Avahi configuration data */
782
server = avahi_server_new(avahi_simple_poll_get(simple_poll),
783
&config, NULL, NULL, &error);
785
/* Free the configuration data */
1206
786
avahi_server_config_free(&config);
1209
/* Check if creating the Avahi server object succeeded */
1210
if(mc.server == NULL){
1211
fprintf(stderr, "Failed to create Avahi server: %s\n",
1212
avahi_strerror(error));
1213
exitcode = EXIT_FAILURE;
1217
/* Create the Avahi service browser */
1218
sb = avahi_s_service_browser_new(mc.server, if_index,
1219
AVAHI_PROTO_INET6, "_mandos._tcp",
1220
NULL, 0, browse_callback, NULL);
1222
fprintf(stderr, "Failed to create service browser: %s\n",
1223
avahi_strerror(avahi_server_errno(mc.server)));
1224
exitcode = EXIT_FAILURE;
1228
sigemptyset(&sigterm_action.sa_mask);
1229
ret = sigaddset(&sigterm_action.sa_mask, SIGTERM);
1231
perror("sigaddset");
1232
exitcode = EXIT_FAILURE;
1235
ret = sigaction(SIGTERM, &sigterm_action, &old_sigterm_action);
1237
perror("sigaction");
1238
exitcode = EXIT_FAILURE;
1242
/* Run the main loop */
1245
fprintf(stderr, "Starting Avahi loop search\n");
1248
avahi_simple_poll_loop(mc.simple_poll);
1253
fprintf(stderr, "%s exiting\n", argv[0]);
1256
/* Cleanup things */
1258
avahi_s_service_browser_free(sb);
1260
if(mc.server != NULL)
1261
avahi_server_free(mc.server);
1263
if(mc.simple_poll != NULL)
1264
avahi_simple_poll_free(mc.simple_poll);
1266
if(gnutls_initialized){
1267
gnutls_certificate_free_credentials(mc.cred);
1268
gnutls_global_deinit();
1269
gnutls_dh_params_deinit(mc.dh_params);
1272
if(gpgme_initialized){
1273
gpgme_release(mc.ctx);
1276
/* Removes the temp directory used by GPGME */
1277
if(tempdir_created){
1279
struct dirent *direntry;
1280
d = opendir(tempdir);
1282
if(errno != ENOENT){
1287
direntry = readdir(d);
1288
if(direntry == NULL){
1291
/* Skip "." and ".." */
1292
if(direntry->d_name[0] == '.'
1293
and (direntry->d_name[1] == '\0'
1294
or (direntry->d_name[1] == '.'
1295
and direntry->d_name[2] == '\0'))){
1298
char *fullname = NULL;
1299
ret = asprintf(&fullname, "%s/%s", tempdir,
1305
ret = remove(fullname);
1307
fprintf(stderr, "remove(\"%s\"): %s\n", fullname,
1314
ret = rmdir(tempdir);
1315
if(ret == -1 and errno != ENOENT){
788
/* Check if creating the server object succeeded */
790
fprintf(stderr, "Failed to create server: %s\n",
791
avahi_strerror(error));
792
returncode = EXIT_FAILURE;
796
/* Create the service browser */
797
sb = avahi_s_service_browser_new(server, if_index,
799
"_mandos._tcp", NULL, 0,
800
browse_callback, server);
802
fprintf(stderr, "Failed to create service browser: %s\n",
803
avahi_strerror(avahi_server_errno(server)));
804
returncode = EXIT_FAILURE;
808
/* Run the main loop */
811
fprintf(stderr, "Starting avahi loop search\n");
814
avahi_simple_poll_loop(simple_poll);
819
fprintf(stderr, "%s exiting\n", argv[0]);
824
avahi_s_service_browser_free(sb);
827
avahi_server_free(server);
830
avahi_simple_poll_free(simple_poll);