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() */
72
#define CERT_ROOT "/conf/conf.d/cryptkeyreq/"
74
#define CERTFILE CERT_ROOT "openpgp-client.txt"
75
#define KEYFILE CERT_ROOT "openpgp-client-key.txt"
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,
76
110
#define BUFFER_SIZE 256
112
#define PATHDIR "/conf/conf.d/mandos"
113
#define SECKEY "seckey.txt"
114
#define PUBKEY "pubkey.txt"
79
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>";
121
/* Used for passing in values through the Avahi callback functions */
82
gnutls_session_t session;
123
AvahiSimplePoll *simple_poll;
83
125
gnutls_certificate_credentials_t cred;
126
unsigned int dh_bits;
84
127
gnutls_dh_params_t dh_params;
88
ssize_t pgp_packet_decrypt (char *packet, size_t packet_size,
89
char **new_packet, const char *homedir){
90
gpgme_data_t dh_crypto, dh_plain;
128
const char *priority;
133
* Make additional room in "buffer" for at least BUFFER_SIZE
134
* additional bytes. "buffer_capacity" is how much is currently
135
* allocated, "buffer_length" is how much is already used.
137
size_t incbuffer(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){
94
ssize_t new_packet_capacity = 0;
95
ssize_t new_packet_length = 0;
96
156
gpgme_engine_info_t engine_info;
99
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");
103
199
gpgme_check_version(NULL);
104
gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
200
rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
201
if(rc != GPG_ERR_NO_ERROR){
202
fprintf(stderr, "bad gpgme_engine_check_version: %s: %s\n",
203
gpgme_strsource(rc), gpgme_strerror(rc));
106
/* Set GPGME home directory */
107
rc = gpgme_get_engine_info (&engine_info);
108
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){
109
210
fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
110
211
gpgme_strsource(rc), gpgme_strerror(rc));
113
214
while(engine_info != NULL){
114
215
if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
115
216
gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
116
engine_info->file_name, homedir);
217
engine_info->file_name, tempdir);
119
220
engine_info = engine_info->next;
121
222
if(engine_info == NULL){
122
fprintf(stderr, "Could not set home dir to %s\n", homedir);
126
/* Create new GPGME data buffer from packet buffer */
127
rc = gpgme_data_new_from_mem(&dh_crypto, packet, packet_size, 0);
128
if (rc != GPG_ERR_NO_ERROR){
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){
129
264
fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
130
265
gpgme_strsource(rc), gpgme_strerror(rc));
134
269
/* Create new empty GPGME data buffer for the plaintext */
135
270
rc = gpgme_data_new(&dh_plain);
136
if (rc != GPG_ERR_NO_ERROR){
271
if(rc != GPG_ERR_NO_ERROR){
137
272
fprintf(stderr, "bad gpgme_data_new: %s: %s\n",
138
273
gpgme_strsource(rc), gpgme_strerror(rc));
142
/* Create new GPGME "context" */
143
rc = gpgme_new(&ctx);
144
if (rc != GPG_ERR_NO_ERROR){
145
fprintf(stderr, "bad gpgme_new: %s: %s\n",
146
gpgme_strsource(rc), gpgme_strerror(rc));
150
/* Decrypt data from the FILE pointer to the plaintext data
152
rc = gpgme_op_decrypt(ctx, dh_crypto, dh_plain);
153
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){
154
282
fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
155
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;
160
fprintf(stderr, "Decryption of OpenPGP packet succeeded\n");
164
gpgme_decrypt_result_t result;
165
result = gpgme_op_decrypt_result(ctx);
167
fprintf(stderr, "gpgme_op_decrypt_result failed\n");
169
fprintf(stderr, "Unsupported algorithm: %s\n",
170
result->unsupported_algorithm);
171
fprintf(stderr, "Wrong key usage: %d\n",
172
result->wrong_key_usage);
173
if(result->file_name != NULL){
174
fprintf(stderr, "File name: %s\n", result->file_name);
176
gpgme_recipient_t recipient;
177
recipient = result->recipients;
179
while(recipient != NULL){
180
fprintf(stderr, "Public key algorithm: %s\n",
181
gpgme_pubkey_algo_name(recipient->pubkey_algo));
182
fprintf(stderr, "Key ID: %s\n", recipient->keyid);
183
fprintf(stderr, "Secret key available: %s\n",
184
recipient->status == GPG_ERR_NO_SECKEY
186
recipient = recipient->next;
192
/* Delete the GPGME FILE pointer cryptotext data buffer */
193
gpgme_data_release(dh_crypto);
317
fprintf(stderr, "Decryption of OpenPGP data succeeded\n");
195
320
/* Seek back to the beginning of the GPGME plaintext data buffer */
196
gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET);
321
if(gpgme_data_seek(dh_plain, (off_t)0, SEEK_SET) == -1){
322
perror("gpgme_data_seek");
323
plaintext_length = -1;
200
if (new_packet_length + BUFFER_SIZE > new_packet_capacity){
201
*new_packet = realloc(*new_packet,
202
(unsigned int)new_packet_capacity
204
if (*new_packet == NULL){
208
new_packet_capacity += BUFFER_SIZE;
329
plaintext_capacity = incbuffer(plaintext,
330
(size_t)plaintext_length,
332
if(plaintext_capacity == 0){
334
plaintext_length = -1;
211
ret = gpgme_data_read(dh_plain, *new_packet + new_packet_length,
338
ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
213
340
/* Print the data, if any */
218
346
perror("gpgme_data_read");
221
new_packet_length += ret;
224
/* FIXME: check characters before printing to screen so to not print
225
terminal control characters */
227
/* fprintf(stderr, "decrypted password is: "); */
228
/* fwrite(*new_packet, 1, new_packet_length, stderr); */
229
/* 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);
232
366
/* Delete the GPGME plaintext data buffer */
233
367
gpgme_data_release(dh_plain);
234
return new_packet_length;
368
return plaintext_length;
237
static const char * safer_gnutls_strerror (int value) {
238
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 */
240
375
ret = "(unknown)";
244
void debuggnutls(__attribute__((unused)) int level,
246
fprintf(stderr, "%s", string);
379
/* GnuTLS log function callback */
380
static void debuggnutls(__attribute__((unused)) int level,
382
fprintf(stderr, "GnuTLS: %s", string);
249
int initgnutls(encrypted_session *es){
385
static int init_gnutls_global(mandos_context *mc,
386
const char *pubkeyfilename,
387
const char *seckeyfilename){
254
391
fprintf(stderr, "Initializing GnuTLS\n");
257
if ((ret = gnutls_global_init ())
258
!= GNUTLS_E_SUCCESS) {
259
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."
264
405
gnutls_global_set_log_level(11);
265
406
gnutls_global_set_log_function(debuggnutls);
268
/* openpgp credentials */
269
if ((ret = gnutls_certificate_allocate_credentials (&es->cred))
270
!= GNUTLS_E_SUCCESS) {
271
fprintf (stderr, "memory error: %s\n",
272
safer_gnutls_strerror(ret));
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();
277
fprintf(stderr, "Attempting to use OpenPGP certificate %s"
278
" and keyfile %s as GnuTLS credentials\n", CERTFILE,
422
fprintf(stderr, "Attempting to use OpenPGP public key %s and"
423
" secret key %s as GnuTLS credentials\n", pubkeyfilename,
282
427
ret = gnutls_certificate_set_openpgp_key_file
283
(es->cred, CERTFILE, KEYFILE, GNUTLS_OPENPGP_FMT_BASE64);
284
if (ret != GNUTLS_E_SUCCESS) {
286
(stderr, "Error[%d] while reading the OpenPGP key pair ('%s',"
288
ret, CERTFILE, KEYFILE);
289
fprintf(stdout, "The Error is: %s\n",
290
safer_gnutls_strerror(ret));
294
//GnuTLS server initialization
295
if ((ret = gnutls_dh_params_init (&es->dh_params))
296
!= GNUTLS_E_SUCCESS) {
297
fprintf (stderr, "Error in dh parameter initialization: %s\n",
298
safer_gnutls_strerror(ret));
302
if ((ret = gnutls_dh_params_generate2 (es->dh_params, DH_BITS))
303
!= GNUTLS_E_SUCCESS) {
304
fprintf (stderr, "Error in prime generation: %s\n",
305
safer_gnutls_strerror(ret));
309
gnutls_certificate_set_dh_params (es->cred, es->dh_params);
311
// GnuTLS session creation
312
if ((ret = gnutls_init (&es->session, GNUTLS_SERVER))
313
!= 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){
314
471
fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
315
472
safer_gnutls_strerror(ret));
318
if ((ret = gnutls_priority_set_direct (es->session, "NORMAL", &err))
319
!= GNUTLS_E_SUCCESS) {
320
fprintf(stderr, "Syntax error at: %s\n", err);
321
fprintf(stderr, "GnuTLS error: %s\n",
322
safer_gnutls_strerror(ret));
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);
326
if ((ret = gnutls_credentials_set
327
(es->session, GNUTLS_CRD_CERTIFICATE, es->cred))
328
!= GNUTLS_E_SUCCESS) {
329
fprintf(stderr, "Error setting a credentials set: %s\n",
487
ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
489
if(ret != GNUTLS_E_SUCCESS){
490
fprintf(stderr, "Error setting GnuTLS credentials: %s\n",
330
491
safer_gnutls_strerror(ret));
492
gnutls_deinit(*session);
334
496
/* ignore client certificate if any. */
335
gnutls_certificate_server_set_request (es->session,
497
gnutls_certificate_server_set_request(*session,
338
gnutls_dh_set_prime_bits (es->session, DH_BITS);
500
gnutls_dh_set_prime_bits(*session, mc->dh_bits);
343
void empty_log(__attribute__((unused)) AvahiLogLevel level,
344
__attribute__((unused)) const char *txt){}
505
/* Avahi log function callback */
506
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
507
__attribute__((unused)) const char *txt){}
346
int start_mandos_communication(const char *ip, uint16_t port,
347
unsigned int if_index){
509
/* Called when a Mandos server is found */
510
static int start_mandos_communication(const char *ip, uint16_t port,
511
AvahiIfIndex if_index,
512
mandos_context *mc, int af){
349
struct sockaddr_in6 to;
350
encrypted_session es;
516
struct sockaddr_in in;
517
struct sockaddr_in6 in6;
351
519
char *buffer = NULL;
352
520
char *decrypted_buffer;
353
521
size_t buffer_length = 0;
354
522
size_t buffer_capacity = 0;
355
523
ssize_t decrypted_buffer_size;
358
char interface[IF_NAMESIZE];
526
gnutls_session_t session;
527
int pf; /* Protocol family */
537
fprintf(stderr, "Bad address family: %d\n", af);
541
ret = init_gnutls_session(mc, &session);
361
fprintf(stderr, "Setting up a tcp connection to %s\n", ip);
547
fprintf(stderr, "Setting up a TCP connection to %s, port %" PRIu16
364
tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
551
tcp_sd = socket(pf, SOCK_STREAM, 0);
366
553
perror("socket");
370
if(if_indextoname(if_index, interface) == NULL){
372
perror("if_indextoname");
378
fprintf(stderr, "Binding to interface %s\n", interface);
381
memset(&to,0,sizeof(to)); /* Spurious warning */
382
to.sin6_family = AF_INET6;
383
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);
385
566
perror("inet_pton");
389
570
fprintf(stderr, "Bad address: %s\n", ip);
392
to.sin6_port = htons(port); /* Spurious warning */
394
to.sin6_scope_id = (uint32_t)if_index;
574
to.in6.sin6_port = htons(port); /* Spurious warnings from
576
-Wunreachable-code */
578
if(IN6_IS_ADDR_LINKLOCAL /* Spurious warnings from */
579
(&to.in6.sin6_addr)){ /* -Wstrict-aliasing=2 or lower and
581
if(if_index == AVAHI_IF_UNSPEC){
582
fprintf(stderr, "An IPv6 link-local address is incomplete"
583
" without a network interface\n");
586
/* Set the network interface number as scope */
587
to.in6.sin6_scope_id = (uint32_t)if_index;
590
to.in.sin_port = htons(port); /* Spurious warnings from
592
-Wunreachable-code */
397
fprintf(stderr, "Connection to: %s\n", ip);
596
if(af == AF_INET6 and if_index != AVAHI_IF_UNSPEC){
597
char interface[IF_NAMESIZE];
598
if(if_indextoname((unsigned int)if_index, interface) == NULL){
599
perror("if_indextoname");
601
fprintf(stderr, "Connection to: %s%%%s, port %" PRIu16 "\n",
602
ip, interface, port);
605
fprintf(stderr, "Connection to: %s, port %" PRIu16 "\n", ip,
608
char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ?
609
INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = "";
612
pcret = inet_ntop(af, &(to.in6.sin6_addr), addrstr,
615
pcret = inet_ntop(af, &(to.in.sin_addr), addrstr,
621
if(strcmp(addrstr, ip) != 0){
622
fprintf(stderr, "Canonical address form: %s\n", addrstr);
400
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 */
402
633
perror("connect");
406
ret = initgnutls (&es);
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){
412
gnutls_transport_set_ptr (es.session,
413
(gnutls_transport_ptr_t) tcp_sd);
416
662
fprintf(stderr, "Establishing TLS session with %s\n", ip);
419
ret = gnutls_handshake (es.session);
421
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){
423
fprintf(stderr, "\n*** Handshake failed ***\n");
673
fprintf(stderr, "*** GnuTLS Handshake failed ***\n");
430
//Retrieve OpenPGP packet that contains the wanted password
680
/* Read OpenPGP packet that contains the wanted password */
433
fprintf(stderr, "Retrieving pgp encrypted password from %s\n",
683
fprintf(stderr, "Retrieving OpenPGP encrypted password from %s\n",
438
if (buffer_length + BUFFER_SIZE > buffer_capacity){
439
buffer = realloc(buffer, buffer_capacity + BUFFER_SIZE);
444
buffer_capacity += BUFFER_SIZE;
688
buffer_capacity = incbuffer(&buffer, buffer_length,
690
if(buffer_capacity == 0){
447
ret = gnutls_record_recv
448
(es.session, buffer+buffer_length, BUFFER_SIZE);
696
sret = gnutls_record_recv(session, buffer+buffer_length,
454
703
case GNUTLS_E_INTERRUPTED:
455
704
case GNUTLS_E_AGAIN:
457
706
case GNUTLS_E_REHANDSHAKE:
458
ret = gnutls_handshake (es.session);
460
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");
467
718
fprintf(stderr, "Unknown error while reading data from"
468
" encrypted session with mandos server\n");
719
" encrypted session with Mandos server\n");
470
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
721
gnutls_bye(session, GNUTLS_SHUT_RDWR);
474
buffer_length += (size_t) ret;
725
buffer_length += (size_t) sret;
478
if (buffer_length > 0){
479
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,
483
if (decrypted_buffer_size >= 0){
484
while(written < decrypted_buffer_size){
485
ret = (int)fwrite (decrypted_buffer + written, 1,
486
(size_t)decrypted_buffer_size - written,
739
if(decrypted_buffer_size >= 0){
741
while(written < (size_t) decrypted_buffer_size){
742
ret = (int)fwrite(decrypted_buffer + written, 1,
743
(size_t)decrypted_buffer_size - written,
488
745
if(ret == 0 and ferror(stdout)){
490
747
fprintf(stderr, "Error writing encrypted data: %s\n",
553
805
char ip[AVAHI_ADDRESS_STR_MAX];
554
806
avahi_address_snprint(ip, sizeof(ip), address);
556
fprintf(stderr, "Mandos server \"%s\" found on %s (%s) on"
557
" 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);
559
int ret = start_mandos_communication(ip, port,
560
(unsigned int) interface);
812
int ret = start_mandos_communication(ip, port, interface, mc,
813
avahi_proto_to_af(proto));
815
avahi_simple_poll_quit(mc->simple_poll);
566
819
avahi_s_service_resolver_free(r);
569
static void browse_callback(
570
AvahiSServiceBrowser *b,
571
AvahiIfIndex interface,
572
AvahiProtocol protocol,
573
AvahiBrowserEvent event,
577
AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
580
AvahiServer *s = userdata;
581
assert(b); /* Spurious warning */
583
/* Called whenever a new services becomes available on the LAN or
584
is removed from the LAN */
588
case AVAHI_BROWSER_FAILURE:
590
fprintf(stderr, "(Browser) %s\n",
591
avahi_strerror(avahi_server_errno(server)));
592
avahi_simple_poll_quit(simple_poll);
595
case AVAHI_BROWSER_NEW:
596
/* We ignore the returned resolver object. In the callback
597
function we free it. If the server is terminated before
598
the callback function is called the server will free
599
the resolver for us. */
601
if (!(avahi_s_service_resolver_new(s, interface, protocol, name,
603
AVAHI_PROTO_INET6, 0,
604
resolve_callback, s)))
605
fprintf(stderr, "Failed to resolve service '%s': %s\n", name,
606
avahi_strerror(avahi_server_errno(s)));
609
case AVAHI_BROWSER_REMOVE:
612
case AVAHI_BROWSER_ALL_FOR_NOW:
613
case AVAHI_BROWSER_CACHE_EXHAUSTED:
822
static void browse_callback(AvahiSServiceBrowser *b,
823
AvahiIfIndex interface,
824
AvahiProtocol protocol,
825
AvahiBrowserEvent event,
829
AVAHI_GCC_UNUSED AvahiLookupResultFlags
832
mandos_context *mc = userdata;
835
/* Called whenever a new services becomes available on the LAN or
836
is removed from the LAN */
840
case AVAHI_BROWSER_FAILURE:
842
fprintf(stderr, "(Avahi browser) %s\n",
843
avahi_strerror(avahi_server_errno(mc->server)));
844
avahi_simple_poll_quit(mc->simple_poll);
847
case AVAHI_BROWSER_NEW:
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
853
if(!(avahi_s_service_resolver_new(mc->server, interface,
854
protocol, name, type, domain,
855
AVAHI_PROTO_INET6, 0,
856
resolve_callback, mc)))
857
fprintf(stderr, "Avahi: Failed to resolve service '%s': %s\n",
858
name, avahi_strerror(avahi_server_errno(mc->server)));
861
case AVAHI_BROWSER_REMOVE:
864
case AVAHI_BROWSER_ALL_FOR_NOW:
865
case AVAHI_BROWSER_CACHE_EXHAUSTED:
867
fprintf(stderr, "No Mandos server found, still searching...\n");
618
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
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",
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");
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;
1008
sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1011
exitcode = EXIT_FAILURE;
1013
if(restore_loglevel){
1014
ret = klogctl(7, NULL, 0);
1022
strcpy(network.ifr_name, interface);
1023
ret = ioctl(sd, SIOCGIFFLAGS, &network);
1025
perror("ioctl SIOCGIFFLAGS");
1027
if(restore_loglevel){
1028
ret = klogctl(7, NULL, 0);
1034
exitcode = EXIT_FAILURE;
1037
if((network.ifr_flags & IFF_UP) == 0){
1038
network.ifr_flags |= IFF_UP;
1039
ret = ioctl(sd, SIOCSIFFLAGS, &network);
1041
perror("ioctl SIOCSIFFLAGS");
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;
619
1182
AvahiServerConfig config;
620
AvahiSServiceBrowser *sb = NULL;
623
int returncode = EXIT_SUCCESS;
624
const char *interface = "eth0";
627
static struct option long_options[] = {
628
{"debug", no_argument, (int *)&debug, 1},
629
{"interface", required_argument, 0, 'i'},
632
int option_index = 0;
633
ret = getopt_long (argc, argv, "i:", long_options,
652
avahi_set_log_function(empty_log);
655
/* Initialize the psuedo-RNG */
656
srand((unsigned int) time(NULL));
658
/* Allocate main loop object */
659
if (!(simple_poll = avahi_simple_poll_new())) {
660
fprintf(stderr, "Failed to create simple poll object.\n");
665
/* Do not publish any local records */
1183
/* Do not publish any local Zeroconf records */
666
1184
avahi_server_config_init(&config);
667
1185
config.publish_hinfo = 0;
668
1186
config.publish_addresses = 0;
669
1187
config.publish_workstation = 0;
670
1188
config.publish_domain = 0;
672
1190
/* Allocate a new server */
673
server = avahi_server_new(avahi_simple_poll_get(simple_poll),
674
&config, NULL, NULL, &error);
676
/* Free the configuration data */
1191
mc.server = avahi_server_new(avahi_simple_poll_get
1192
(mc.simple_poll), &config, NULL,
1195
/* Free the Avahi configuration data */
677
1196
avahi_server_config_free(&config);
679
/* Check if creating the server object succeeded */
681
fprintf(stderr, "Failed to create server: %s\n",
682
avahi_strerror(error));
683
returncode = EXIT_FAILURE;
687
/* Create the service browser */
688
sb = avahi_s_service_browser_new(server,
690
if_nametoindex(interface),
692
"_mandos._tcp", NULL, 0,
693
browse_callback, server);
695
fprintf(stderr, "Failed to create service browser: %s\n",
696
avahi_strerror(avahi_server_errno(server)));
697
returncode = EXIT_FAILURE;
701
/* Run the main loop */
704
fprintf(stderr, "Starting avahi loop search\n");
707
avahi_simple_poll_loop(simple_poll);
712
fprintf(stderr, "%s exiting\n", argv[0]);
717
avahi_s_service_browser_free(sb);
720
avahi_server_free(server);
723
avahi_simple_poll_free(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){