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, false, true */
46
#include <string.h> /* memset(), strcmp(), strlen(),
47
strerror(), asprintf(), strcpy() */
48
#include <sys/ioctl.h> /* ioctl */
49
#include <sys/types.h> /* socket(), inet_pton(), sockaddr,
50
sockaddr_in6, PF_INET6,
51
SOCK_STREAM, uid_t, gid_t, open(),
53
#include <sys/stat.h> /* open() */
54
#include <sys/socket.h> /* socket(), struct sockaddr_in6,
55
inet_pton(), connect() */
56
#include <fcntl.h> /* open() */
57
#include <dirent.h> /* opendir(), struct dirent, readdir()
59
#include <inttypes.h> /* PRIu16, intmax_t, SCNdMAX */
60
#include <assert.h> /* assert() */
61
#include <errno.h> /* perror(), errno */
62
#include <time.h> /* nanosleep(), time() */
63
#include <net/if.h> /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
64
SIOCSIFFLAGS, if_indextoname(),
65
if_nametoindex(), IF_NAMESIZE */
66
#include <netinet/in.h> /* IN6_IS_ADDR_LINKLOCAL,
67
INET_ADDRSTRLEN, INET6_ADDRSTRLEN
69
#include <unistd.h> /* close(), SEEK_SET, off_t, write(),
70
getuid(), getgid(), setuid(),
72
#include <arpa/inet.h> /* inet_pton(), htons */
73
#include <iso646.h> /* not, or, and */
74
#include <argp.h> /* struct argp_option, error_t, struct
75
argp_state, struct argp,
76
argp_parse(), ARGP_KEY_ARG,
77
ARGP_KEY_END, ARGP_ERR_UNKNOWN */
78
#include <signal.h> /* sigemptyset(), sigaddset(), sigaction(), SIGTERM, sigaction */
80
#include <sys/klog.h> /* klogctl() */
84
/* All Avahi types, constants and functions
39
#include <net/if.h> /* if_nametoindex */
40
#include <sys/ioctl.h> // ioctl, ifreq, SIOCGIFFLAGS, IFF_UP, SIOCSIFFLAGS
41
#include <net/if.h> // ioctl, ifreq, SIOCGIFFLAGS, IFF_UP, SIOCSIFFLAGS
87
43
#include <avahi-core/core.h>
88
44
#include <avahi-core/lookup.h>
89
45
#include <avahi-core/log.h>
91
47
#include <avahi-common/malloc.h>
92
48
#include <avahi-common/error.h>
95
#include <gnutls/gnutls.h> /* All GnuTLS types, constants and
98
init_gnutls_session(),
100
#include <gnutls/openpgp.h>
101
/* gnutls_certificate_set_openpgp_key_file(),
102
GNUTLS_OPENPGP_FMT_BASE64 */
105
#include <gpgme.h> /* All GPGME types, constants and
108
GPGME_PROTOCOL_OpenPGP,
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() */
111
71
#define BUFFER_SIZE 256
113
#define PATHDIR "/conf/conf.d/mandos"
114
#define SECKEY "seckey.txt"
115
#define PUBKEY "pubkey.txt"
73
static const char *keydir = "/conf/conf.d/mandos";
74
static const char *pubkeyfile = "pubkey.txt";
75
static const char *seckeyfile = "seckey.txt";
117
77
bool debug = false;
118
static const char mandos_protocol_version[] = "1";
119
const char *argp_program_version = "mandos-client " VERSION;
120
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
122
/* Used for passing in values through the Avahi callback functions */
79
/* Used for passing in values through all the callback functions */
124
81
AvahiSimplePoll *simple_poll;
125
82
AvahiServer *server;
126
83
gnutls_certificate_credentials_t cred;
127
84
unsigned int dh_bits;
128
gnutls_dh_params_t dh_params;
129
85
const char *priority;
88
static ssize_t pgp_packet_decrypt (char *packet, size_t packet_size,
91
gpgme_data_t dh_crypto, dh_plain;
133
/* global context so signal handler can reach it*/
137
* Make additional room in "buffer" for at least BUFFER_SIZE
138
* additional bytes. "buffer_capacity" is how much is currently
139
* allocated, "buffer_length" is how much is already used.
141
size_t incbuffer(char **buffer, size_t buffer_length,
142
size_t buffer_capacity){
143
if(buffer_length + BUFFER_SIZE > buffer_capacity){
144
*buffer = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
148
buffer_capacity += BUFFER_SIZE;
150
return buffer_capacity;
156
static bool init_gpgme(const char *seckey,
157
const char *pubkey, const char *tempdir){
95
ssize_t new_packet_capacity = 0;
96
ssize_t new_packet_length = 0;
160
97
gpgme_engine_info_t engine_info;
164
* Helper function to insert pub and seckey to the engine keyring.
166
bool import_key(const char *filename){
168
gpgme_data_t pgp_data;
170
fd = (int)TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
176
rc = gpgme_data_new_from_fd(&pgp_data, fd);
177
if(rc != GPG_ERR_NO_ERROR){
178
fprintf(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
179
gpgme_strsource(rc), gpgme_strerror(rc));
183
rc = gpgme_op_import(mc.ctx, pgp_data);
184
if(rc != GPG_ERR_NO_ERROR){
185
fprintf(stderr, "bad gpgme_op_import: %s: %s\n",
186
gpgme_strsource(rc), gpgme_strerror(rc));
190
ret = (int)TEMP_FAILURE_RETRY(close(fd));
194
gpgme_data_release(pgp_data);
199
fprintf(stderr, "Initialize gpgme\n");
100
fprintf(stderr, "Trying to decrypt OpenPGP packet\n");
203
104
gpgme_check_version(NULL);
204
105
rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
205
if(rc != GPG_ERR_NO_ERROR){
106
if (rc != GPG_ERR_NO_ERROR){
206
107
fprintf(stderr, "bad gpgme_engine_check_version: %s: %s\n",
207
108
gpgme_strsource(rc), gpgme_strerror(rc));
211
/* Set GPGME home directory for the OpenPGP engine only */
212
rc = gpgme_get_engine_info(&engine_info);
213
if(rc != GPG_ERR_NO_ERROR){
112
/* Set GPGME home directory */
113
rc = gpgme_get_engine_info (&engine_info);
114
if (rc != GPG_ERR_NO_ERROR){
214
115
fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
215
116
gpgme_strsource(rc), gpgme_strerror(rc));
218
119
while(engine_info != NULL){
219
120
if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
220
121
gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
221
engine_info->file_name, tempdir);
122
engine_info->file_name, homedir);
224
125
engine_info = engine_info->next;
226
127
if(engine_info == NULL){
227
fprintf(stderr, "Could not set GPGME home dir to %s\n", tempdir);
231
/* Create new GPGME "context" */
232
rc = gpgme_new(&(mc.ctx));
233
if(rc != GPG_ERR_NO_ERROR){
234
fprintf(stderr, "bad gpgme_new: %s: %s\n",
235
gpgme_strsource(rc), gpgme_strerror(rc));
239
if(not import_key(pubkey) or not import_key(seckey)){
247
* Decrypt OpenPGP data.
248
* Returns -1 on error
250
static ssize_t pgp_packet_decrypt(const char *cryptotext,
253
gpgme_data_t dh_crypto, dh_plain;
256
size_t plaintext_capacity = 0;
257
ssize_t plaintext_length = 0;
260
fprintf(stderr, "Trying to decrypt OpenPGP data\n");
263
/* Create new GPGME data buffer from memory cryptotext */
264
rc = gpgme_data_new_from_mem(&dh_crypto, cryptotext, crypto_size,
266
if(rc != GPG_ERR_NO_ERROR){
128
fprintf(stderr, "Could not set home dir to %s\n", homedir);
132
/* Create new GPGME data buffer from packet buffer */
133
rc = gpgme_data_new_from_mem(&dh_crypto, packet, packet_size, 0);
134
if (rc != GPG_ERR_NO_ERROR){
267
135
fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
268
136
gpgme_strsource(rc), gpgme_strerror(rc));
272
140
/* Create new empty GPGME data buffer for the plaintext */
273
141
rc = gpgme_data_new(&dh_plain);
274
if(rc != GPG_ERR_NO_ERROR){
142
if (rc != GPG_ERR_NO_ERROR){
275
143
fprintf(stderr, "bad gpgme_data_new: %s: %s\n",
276
144
gpgme_strsource(rc), gpgme_strerror(rc));
277
gpgme_data_release(dh_crypto);
281
/* Decrypt data from the cryptotext data buffer to the plaintext
283
rc = gpgme_op_decrypt(mc.ctx, dh_crypto, dh_plain);
284
if(rc != GPG_ERR_NO_ERROR){
148
/* Create new GPGME "context" */
149
rc = gpgme_new(&ctx);
150
if (rc != GPG_ERR_NO_ERROR){
151
fprintf(stderr, "bad gpgme_new: %s: %s\n",
152
gpgme_strsource(rc), gpgme_strerror(rc));
156
/* Decrypt data from the FILE pointer to the plaintext data
158
rc = gpgme_op_decrypt(ctx, dh_crypto, dh_plain);
159
if (rc != GPG_ERR_NO_ERROR){
285
160
fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
286
161
gpgme_strsource(rc), gpgme_strerror(rc));
287
plaintext_length = -1;
289
gpgme_decrypt_result_t result;
290
result = gpgme_op_decrypt_result(mc.ctx);
292
fprintf(stderr, "gpgme_op_decrypt_result failed\n");
294
fprintf(stderr, "Unsupported algorithm: %s\n",
295
result->unsupported_algorithm);
296
fprintf(stderr, "Wrong key usage: %u\n",
297
result->wrong_key_usage);
298
if(result->file_name != NULL){
299
fprintf(stderr, "File name: %s\n", result->file_name);
301
gpgme_recipient_t recipient;
302
recipient = result->recipients;
304
while(recipient != NULL){
305
fprintf(stderr, "Public key algorithm: %s\n",
306
gpgme_pubkey_algo_name(recipient->pubkey_algo));
307
fprintf(stderr, "Key ID: %s\n", recipient->keyid);
308
fprintf(stderr, "Secret key available: %s\n",
309
recipient->status == GPG_ERR_NO_SECKEY
311
recipient = recipient->next;
166
fprintf(stderr, "Decryption of OpenPGP packet succeeded\n");
170
gpgme_decrypt_result_t result;
171
result = gpgme_op_decrypt_result(ctx);
173
fprintf(stderr, "gpgme_op_decrypt_result failed\n");
175
fprintf(stderr, "Unsupported algorithm: %s\n",
176
result->unsupported_algorithm);
177
fprintf(stderr, "Wrong key usage: %d\n",
178
result->wrong_key_usage);
179
if(result->file_name != NULL){
180
fprintf(stderr, "File name: %s\n", result->file_name);
182
gpgme_recipient_t recipient;
183
recipient = result->recipients;
185
while(recipient != NULL){
186
fprintf(stderr, "Public key algorithm: %s\n",
187
gpgme_pubkey_algo_name(recipient->pubkey_algo));
188
fprintf(stderr, "Key ID: %s\n", recipient->keyid);
189
fprintf(stderr, "Secret key available: %s\n",
190
recipient->status == GPG_ERR_NO_SECKEY
192
recipient = recipient->next;
320
fprintf(stderr, "Decryption of OpenPGP data succeeded\n");
198
/* Delete the GPGME FILE pointer cryptotext data buffer */
199
gpgme_data_release(dh_crypto);
323
201
/* Seek back to the beginning of the GPGME plaintext data buffer */
324
if(gpgme_data_seek(dh_plain, (off_t)0, SEEK_SET) == -1){
325
perror("gpgme_data_seek");
326
plaintext_length = -1;
202
if (gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET) == -1){
203
perror("pgpme_data_seek");
332
plaintext_capacity = incbuffer(plaintext,
333
(size_t)plaintext_length,
335
if(plaintext_capacity == 0){
337
plaintext_length = -1;
208
if (new_packet_length + BUFFER_SIZE > new_packet_capacity){
209
*new_packet = realloc(*new_packet,
210
(unsigned int)new_packet_capacity
212
if (*new_packet == NULL){
216
new_packet_capacity += BUFFER_SIZE;
341
ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
219
ret = gpgme_data_read(dh_plain, *new_packet + new_packet_length,
343
221
/* Print the data, if any */
349
226
perror("gpgme_data_read");
350
plaintext_length = -1;
353
plaintext_length += ret;
357
fprintf(stderr, "Decrypted password is: ");
358
for(ssize_t i = 0; i < plaintext_length; i++){
359
fprintf(stderr, "%02hhX ", (*plaintext)[i]);
361
fprintf(stderr, "\n");
366
/* Delete the GPGME cryptotext data buffer */
367
gpgme_data_release(dh_crypto);
229
new_packet_length += ret;
232
/* FIXME: check characters before printing to screen so to not print
233
terminal control characters */
235
/* fprintf(stderr, "decrypted password is: "); */
236
/* fwrite(*new_packet, 1, new_packet_length, stderr); */
237
/* fprintf(stderr, "\n"); */
369
240
/* Delete the GPGME plaintext data buffer */
370
241
gpgme_data_release(dh_plain);
371
return plaintext_length;
242
return new_packet_length;
374
static const char * safer_gnutls_strerror(int value){
375
const char *ret = gnutls_strerror(value); /* Spurious warning from
376
-Wunreachable-code */
245
static const char * safer_gnutls_strerror (int value) {
246
const char *ret = gnutls_strerror (value);
378
248
ret = "(unknown)";
382
/* GnuTLS log function callback */
383
252
static void debuggnutls(__attribute__((unused)) int level,
384
253
const char* string){
385
fprintf(stderr, "GnuTLS: %s", string);
254
fprintf(stderr, "%s", string);
388
static int init_gnutls_global(const char *pubkeyfilename,
389
const char *seckeyfilename){
257
static int initgnutls(mandos_context *mc, gnutls_session_t *session,
258
gnutls_dh_params_t *dh_params){
393
263
fprintf(stderr, "Initializing GnuTLS\n");
396
ret = gnutls_global_init();
397
if(ret != GNUTLS_E_SUCCESS){
398
fprintf(stderr, "GnuTLS global_init: %s\n",
399
safer_gnutls_strerror(ret));
266
if ((ret = gnutls_global_init ())
267
!= GNUTLS_E_SUCCESS) {
268
fprintf (stderr, "global_init: %s\n", safer_gnutls_strerror(ret));
404
/* "Use a log level over 10 to enable all debugging options."
407
273
gnutls_global_set_log_level(11);
408
274
gnutls_global_set_log_function(debuggnutls);
411
/* OpenPGP credentials */
412
gnutls_certificate_allocate_credentials(&mc.cred);
413
if(ret != GNUTLS_E_SUCCESS){
414
fprintf(stderr, "GnuTLS memory error: %s\n", /* Spurious warning
418
safer_gnutls_strerror(ret));
419
gnutls_global_deinit();
277
/* openpgp credentials */
278
if ((ret = gnutls_certificate_allocate_credentials (&mc->cred))
279
!= GNUTLS_E_SUCCESS) {
280
fprintf (stderr, "memory error: %s\n",
281
safer_gnutls_strerror(ret));
424
fprintf(stderr, "Attempting to use OpenPGP public key %s and"
425
" secret key %s as GnuTLS credentials\n", pubkeyfilename,
286
fprintf(stderr, "Attempting to use OpenPGP certificate %s"
287
" and keyfile %s as GnuTLS credentials\n", pubkeyfile,
429
291
ret = gnutls_certificate_set_openpgp_key_file
430
(mc.cred, pubkeyfilename, seckeyfilename,
431
GNUTLS_OPENPGP_FMT_BASE64);
432
if(ret != GNUTLS_E_SUCCESS){
434
"Error[%d] while reading the OpenPGP key pair ('%s',"
435
" '%s')\n", ret, pubkeyfilename, seckeyfilename);
436
fprintf(stderr, "The GnuTLS error is: %s\n",
437
safer_gnutls_strerror(ret));
441
/* GnuTLS server initialization */
442
ret = gnutls_dh_params_init(&mc.dh_params);
443
if(ret != GNUTLS_E_SUCCESS){
444
fprintf(stderr, "Error in GnuTLS DH parameter initialization:"
445
" %s\n", safer_gnutls_strerror(ret));
448
ret = gnutls_dh_params_generate2(mc.dh_params, mc.dh_bits);
449
if(ret != GNUTLS_E_SUCCESS){
450
fprintf(stderr, "Error in GnuTLS prime generation: %s\n",
451
safer_gnutls_strerror(ret));
455
gnutls_certificate_set_dh_params(mc.cred, mc.dh_params);
461
gnutls_certificate_free_credentials(mc.cred);
462
gnutls_global_deinit();
463
gnutls_dh_params_deinit(mc.dh_params);
467
static int init_gnutls_session(gnutls_session_t *session){
469
/* GnuTLS session creation */
470
ret = gnutls_init(session, GNUTLS_SERVER);
471
if(ret != GNUTLS_E_SUCCESS){
292
(mc->cred, pubkeyfile, seckeyfile, GNUTLS_OPENPGP_FMT_BASE64);
293
if (ret != GNUTLS_E_SUCCESS) {
295
(stderr, "Error[%d] while reading the OpenPGP key pair ('%s',"
297
ret, pubkeyfile, seckeyfile);
298
fprintf(stdout, "The Error is: %s\n",
299
safer_gnutls_strerror(ret));
303
//GnuTLS server initialization
304
if ((ret = gnutls_dh_params_init(dh_params))
305
!= GNUTLS_E_SUCCESS) {
306
fprintf (stderr, "Error in dh parameter initialization: %s\n",
307
safer_gnutls_strerror(ret));
311
if ((ret = gnutls_dh_params_generate2(*dh_params, mc->dh_bits))
312
!= GNUTLS_E_SUCCESS) {
313
fprintf (stderr, "Error in prime generation: %s\n",
314
safer_gnutls_strerror(ret));
318
gnutls_certificate_set_dh_params(mc->cred, *dh_params);
320
// GnuTLS session creation
321
if ((ret = gnutls_init(session, GNUTLS_SERVER))
322
!= GNUTLS_E_SUCCESS){
472
323
fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
473
324
safer_gnutls_strerror(ret));
478
ret = gnutls_priority_set_direct(*session, mc.priority, &err);
479
if(ret != GNUTLS_E_SUCCESS){
480
fprintf(stderr, "Syntax error at: %s\n", err);
481
fprintf(stderr, "GnuTLS error: %s\n",
482
safer_gnutls_strerror(ret));
483
gnutls_deinit(*session);
327
if ((ret = gnutls_priority_set_direct(*session, mc->priority, &err))
328
!= GNUTLS_E_SUCCESS) {
329
fprintf(stderr, "Syntax error at: %s\n", err);
330
fprintf(stderr, "GnuTLS error: %s\n",
331
safer_gnutls_strerror(ret));
488
ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
490
if(ret != GNUTLS_E_SUCCESS){
491
fprintf(stderr, "Error setting GnuTLS credentials: %s\n",
335
if ((ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
337
!= GNUTLS_E_SUCCESS) {
338
fprintf(stderr, "Error setting a credentials set: %s\n",
492
339
safer_gnutls_strerror(ret));
493
gnutls_deinit(*session);
497
343
/* ignore client certificate if any. */
498
gnutls_certificate_server_set_request(*session,
344
gnutls_certificate_server_set_request (*session,
501
gnutls_dh_set_prime_bits(*session, mc.dh_bits);
347
gnutls_dh_set_prime_bits (*session, mc->dh_bits);
506
/* Avahi log function callback */
507
352
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
508
353
__attribute__((unused)) const char *txt){}
510
/* Called when a Mandos server is found */
511
355
static int start_mandos_communication(const char *ip, uint16_t port,
512
356
AvahiIfIndex if_index,
517
struct sockaddr_in in;
518
struct sockaddr_in6 in6;
359
struct sockaddr_in6 to;
520
360
char *buffer = NULL;
521
361
char *decrypted_buffer;
522
362
size_t buffer_length = 0;
523
363
size_t buffer_capacity = 0;
524
364
ssize_t decrypted_buffer_size;
367
char interface[IF_NAMESIZE];
527
368
gnutls_session_t session;
528
int pf; /* Protocol family */
538
fprintf(stderr, "Bad address family: %d\n", af);
542
ret = init_gnutls_session(&session);
369
gnutls_dh_params_t dh_params;
548
fprintf(stderr, "Setting up a TCP connection to %s, port %" PRIu16
372
fprintf(stderr, "Setting up a tcp connection to %s, port %d\n",
552
tcp_sd = socket(pf, SOCK_STREAM, 0);
376
tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
554
378
perror("socket");
383
if(if_indextoname((unsigned int)if_index, interface) == NULL){
384
perror("if_indextoname");
387
fprintf(stderr, "Binding to interface %s\n", interface);
558
memset(&to, 0, sizeof(to));
560
to.in6.sin6_family = (uint16_t)af;
561
ret = inet_pton(af, ip, &to.in6.sin6_addr);
563
to.in.sin_family = (sa_family_t)af;
564
ret = inet_pton(af, ip, &to.in.sin_addr);
390
memset(&to,0,sizeof(to)); /* Spurious warning */
391
to.sin6_family = AF_INET6;
392
ret = inet_pton(AF_INET6, ip, &to.sin6_addr);
567
394
perror("inet_pton");
629
ret = connect(tcp_sd, &to.in6, sizeof(to));
631
ret = connect(tcp_sd, &to.in, sizeof(to)); /* IPv4 */
418
ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
634
420
perror("connect");
638
const char *out = mandos_protocol_version;
641
size_t out_size = strlen(out);
642
ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
643
out_size - written));
649
written += (size_t)ret;
650
if(written < out_size){
653
if(out == mandos_protocol_version){
424
ret = initgnutls (mc, &session, &dh_params);
430
gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t) tcp_sd);
663
433
fprintf(stderr, "Establishing TLS session with %s\n", ip);
666
gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t) tcp_sd);
669
ret = gnutls_handshake(session);
670
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
672
if(ret != GNUTLS_E_SUCCESS){
436
ret = gnutls_handshake (session);
438
if (ret != GNUTLS_E_SUCCESS){
674
fprintf(stderr, "*** GnuTLS Handshake failed ***\n");
440
fprintf(stderr, "\n*** Handshake failed ***\n");
681
/* Read OpenPGP packet that contains the wanted password */
447
//Retrieve OpenPGP packet that contains the wanted password
684
fprintf(stderr, "Retrieving OpenPGP encrypted password from %s\n",
450
fprintf(stderr, "Retrieving pgp encrypted password from %s\n",
689
buffer_capacity = incbuffer(&buffer, buffer_length,
691
if(buffer_capacity == 0){
455
if (buffer_length + BUFFER_SIZE > buffer_capacity){
456
buffer = realloc(buffer, buffer_capacity + BUFFER_SIZE);
461
buffer_capacity += BUFFER_SIZE;
697
sret = gnutls_record_recv(session, buffer+buffer_length,
464
ret = gnutls_record_recv(session, buffer+buffer_length,
704
471
case GNUTLS_E_INTERRUPTED:
705
472
case GNUTLS_E_AGAIN:
707
474
case GNUTLS_E_REHANDSHAKE:
709
ret = gnutls_handshake(session);
710
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
712
fprintf(stderr, "*** GnuTLS Re-handshake failed ***\n");
475
ret = gnutls_handshake (session);
477
fprintf(stderr, "\n*** Handshake failed ***\n");
719
484
fprintf(stderr, "Unknown error while reading data from"
720
" encrypted session with Mandos server\n");
485
" encrypted session with mandos server\n");
722
gnutls_bye(session, GNUTLS_SHUT_RDWR);
487
gnutls_bye (session, GNUTLS_SHUT_RDWR);
726
buffer_length += (size_t) sret;
491
buffer_length += (size_t) ret;
731
fprintf(stderr, "Closing TLS session\n");
734
gnutls_bye(session, GNUTLS_SHUT_RDWR);
736
if(buffer_length > 0){
495
if (buffer_length > 0){
737
496
decrypted_buffer_size = pgp_packet_decrypt(buffer,
740
if(decrypted_buffer_size >= 0){
500
if (decrypted_buffer_size >= 0){
742
501
while(written < (size_t) decrypted_buffer_size){
743
ret = (int)fwrite(decrypted_buffer + written, 1,
744
(size_t)decrypted_buffer_size - written,
502
ret = (int)fwrite (decrypted_buffer + written, 1,
503
(size_t)decrypted_buffer_size - written,
746
505
if(ret == 0 and ferror(stdout)){
748
507
fprintf(stderr, "Error writing encrypted data: %s\n",
805
566
char ip[AVAHI_ADDRESS_STR_MAX];
806
567
avahi_address_snprint(ip, sizeof(ip), address);
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);
569
fprintf(stderr, "Mandos server \"%s\" found on %s (%s) on"
570
" port %d\n", name, host_name, ip, port);
812
int ret = start_mandos_communication(ip, port, interface,
813
avahi_proto_to_af(proto));
815
avahi_simple_poll_quit(mc.simple_poll);
572
int ret = start_mandos_communication(ip, port, interface, mc);
819
578
avahi_s_service_resolver_free(r);
822
static void browse_callback(AvahiSServiceBrowser *b,
823
AvahiIfIndex interface,
824
AvahiProtocol protocol,
825
AvahiBrowserEvent event,
829
AVAHI_GCC_UNUSED AvahiLookupResultFlags
831
__attribute__((unused)) void* userdata){
581
static void browse_callback( AvahiSServiceBrowser *b,
582
AvahiIfIndex interface,
583
AvahiProtocol protocol,
584
AvahiBrowserEvent event,
588
AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
590
mandos_context *mc = userdata;
591
assert(b); /* Spurious warning */
834
593
/* Called whenever a new services becomes available on the LAN or
835
594
is removed from the LAN */
839
598
case AVAHI_BROWSER_FAILURE:
841
fprintf(stderr, "(Avahi browser) %s\n",
842
avahi_strerror(avahi_server_errno(mc.server)));
843
avahi_simple_poll_quit(mc.simple_poll);
600
fprintf(stderr, "(Browser) %s\n",
601
avahi_strerror(avahi_server_errno(mc->server)));
602
avahi_simple_poll_quit(mc->simple_poll);
846
605
case AVAHI_BROWSER_NEW:
847
/* We ignore the returned Avahi resolver object. In the callback
848
function we free it. If the Avahi server is terminated before
849
the callback function is called the Avahi server will free the
606
/* We ignore the returned resolver object. In the callback
607
function we free it. If the server is terminated before
608
the callback function is called the server will free
609
the resolver for us. */
852
if(!(avahi_s_service_resolver_new(mc.server, interface,
853
protocol, name, type, domain,
611
if (!(avahi_s_service_resolver_new(mc->server, interface, protocol, name,
854
613
AVAHI_PROTO_INET6, 0,
855
resolve_callback, NULL)))
856
fprintf(stderr, "Avahi: Failed to resolve service '%s': %s\n",
857
name, avahi_strerror(avahi_server_errno(mc.server)));
614
resolve_callback, mc)))
615
fprintf(stderr, "Failed to resolve service '%s': %s\n", name,
616
avahi_strerror(avahi_server_errno(mc->server)));
860
619
case AVAHI_BROWSER_REMOVE:
863
622
case AVAHI_BROWSER_ALL_FOR_NOW:
864
623
case AVAHI_BROWSER_CACHE_EXHAUSTED:
866
fprintf(stderr, "No Mandos server found, still searching...\n");
872
static void handle_sigterm(__attribute__((unused)) int sig){
873
int old_errno = errno;
874
avahi_simple_poll_quit(mc.simple_poll);
628
/* Combines file name and path and returns the malloced new
629
string. some sane checks could/should be added */
630
static const char *combinepath(const char *first, const char *second){
631
size_t f_len = strlen(first);
632
size_t s_len = strlen(second);
633
char *tmp = malloc(f_len + s_len + 2);
638
memcpy(tmp, first, f_len); /* Spurious warning */
642
memcpy(tmp + f_len + 1, second, s_len); /* Spurious warning */
644
tmp[f_len + 1 + s_len] = '\0';
878
int main(int argc, char *argv[]){
879
AvahiSServiceBrowser *sb = NULL;
884
int exitcode = EXIT_SUCCESS;
885
const char *interface = "eth0";
886
struct ifreq network;
890
char *connect_to = NULL;
891
char tempdir[] = "/tmp/mandosXXXXXX";
892
bool tempdir_created = false;
893
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
894
const char *seckey = PATHDIR "/" SECKEY;
895
const char *pubkey = PATHDIR "/" PUBKEY;
897
bool gnutls_initialized = false;
898
bool gpgme_initialized = false;
901
struct sigaction old_sigterm_action;
902
struct sigaction sigterm_action = { .sa_handler = handle_sigterm };
904
/* Initialize mandos context */
905
mc = (mandos_context){ .simple_poll = NULL, .server = NULL,
906
.dh_bits = 1024, .priority = "SECURE256"
907
":!CTYPE-X.509:+CTYPE-OPENPGP" };
910
struct argp_option options[] = {
911
{ .name = "debug", .key = 128,
912
.doc = "Debug mode", .group = 3 },
913
{ .name = "connect", .key = 'c',
914
.arg = "ADDRESS:PORT",
915
.doc = "Connect directly to a specific Mandos server",
917
{ .name = "interface", .key = 'i',
919
.doc = "Network interface that will be used to search for"
922
{ .name = "seckey", .key = 's',
924
.doc = "OpenPGP secret key file base name",
926
{ .name = "pubkey", .key = 'p',
928
.doc = "OpenPGP public key file base name",
930
{ .name = "dh-bits", .key = 129,
932
.doc = "Bit length of the prime number used in the"
933
" Diffie-Hellman key exchange",
935
{ .name = "priority", .key = 130,
937
.doc = "GnuTLS priority string for the TLS handshake",
939
{ .name = "delay", .key = 131,
941
.doc = "Maximum delay to wait for interface startup",
649
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
650
AvahiServerConfig config;
651
AvahiSServiceBrowser *sb = NULL;
655
int returncode = EXIT_SUCCESS;
656
const char *interface = "eth0";
657
struct ifreq network;
659
char *connect_to = NULL;
660
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
661
mandos_context mc = { .simple_poll = NULL, .server = NULL,
662
.dh_bits = 1024, .priority = "SECURE256"};
946
error_t parse_opt(int key, char *arg,
947
struct argp_state *state){
949
case 128: /* --debug */
952
case 'c': /* --connect */
955
case 'i': /* --interface */
958
case 's': /* --seckey */
961
case 'p': /* --pubkey */
964
case 129: /* --dh-bits */
965
ret = sscanf(arg, "%" SCNdMAX "%n", &tmpmax, &numchars);
966
if(ret < 1 or tmpmax != (typeof(mc.dh_bits))tmpmax
967
or arg[numchars] != '\0'){
968
fprintf(stderr, "Bad number of DH bits\n");
971
mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
973
case 130: /* --priority */
976
case 131: /* --delay */
977
ret = sscanf(arg, "%lf%n", &delay, &numchars);
978
if(ret < 1 or arg[numchars] != '\0'){
979
fprintf(stderr, "Bad delay\n");
664
debug_int = debug ? 1 : 0;
666
struct option long_options[] = {
667
{"debug", no_argument, &debug_int, 1},
668
{"connect", required_argument, NULL, 'c'},
669
{"interface", required_argument, NULL, 'i'},
670
{"keydir", required_argument, NULL, 'd'},
671
{"seckey", required_argument, NULL, 's'},
672
{"pubkey", required_argument, NULL, 'p'},
673
{"dh-bits", required_argument, NULL, 'D'},
674
{"priority", required_argument, NULL, 'P'},
677
int option_index = 0;
678
ret = getopt_long (argc, argv, "i:", long_options,
705
mc.dh_bits = (unsigned int) strtol(optarg, NULL, 10);
712
mc.priority = optarg;
988
return ARGP_ERR_UNKNOWN;
993
struct argp argp = { .options = options, .parser = parse_opt,
995
.doc = "Mandos client -- Get and decrypt"
996
" passwords from a Mandos server" };
997
ret = argp_parse(&argp, argc, argv, 0, 0, NULL);
998
if(ret == ARGP_ERR_UNKNOWN){
999
fprintf(stderr, "Unknown error while parsing arguments\n");
1000
exitcode = EXIT_FAILURE;
1005
/* If the interface is down, bring it up */
1006
if(interface[0] != '\0'){
1008
/* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
1009
messages to mess up the prompt */
1010
ret = klogctl(8, NULL, 5);
1011
bool restore_loglevel = true;
1013
restore_loglevel = false;
719
debug = debug_int ? true : false;
721
pubkeyfile = combinepath(keydir, pubkeyfile);
722
if (pubkeyfile == NULL){
723
perror("combinepath");
724
returncode = EXIT_FAILURE;
728
seckeyfile = combinepath(keydir, seckeyfile);
729
if (seckeyfile == NULL){
730
perror("combinepath");
734
if_index = (AvahiIfIndex) if_nametoindex(interface);
736
fprintf(stderr, "No such interface: \"%s\"\n", interface);
740
if(connect_to != NULL){
741
/* Connect directly, do not use Zeroconf */
742
/* (Mainly meant for debugging) */
743
char *address = strrchr(connect_to, ':');
745
fprintf(stderr, "No colon in address\n");
749
uint16_t port = (uint16_t) strtol(address+1, NULL, 10);
751
perror("Bad port number");
755
address = connect_to;
756
ret = start_mandos_communication(address, port, if_index, &mc);
1018
764
sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1020
766
perror("socket");
1021
exitcode = EXIT_FAILURE;
1023
if(restore_loglevel){
1024
ret = klogctl(7, NULL, 0);
767
returncode = EXIT_FAILURE;
1032
strcpy(network.ifr_name, interface);
770
strcpy(network.ifr_name, interface); /* Spurious warning */
1033
771
ret = ioctl(sd, SIOCGIFFLAGS, &network);
1035
774
perror("ioctl SIOCGIFFLAGS");
1037
if(restore_loglevel){
1038
ret = klogctl(7, NULL, 0);
1044
exitcode = EXIT_FAILURE;
775
returncode = EXIT_FAILURE;
1047
778
if((network.ifr_flags & IFF_UP) == 0){
1048
779
network.ifr_flags |= IFF_UP;
1049
780
ret = ioctl(sd, SIOCSIFFLAGS, &network);
1051
782
perror("ioctl SIOCSIFFLAGS");
1052
exitcode = EXIT_FAILURE;
1054
if(restore_loglevel){
1055
ret = klogctl(7, NULL, 0);
1064
/* sleep checking until interface is running */
1065
for(int i=0; i < delay * 4; i++){
1066
ret = ioctl(sd, SIOCGIFFLAGS, &network);
1068
perror("ioctl SIOCGIFFLAGS");
1069
} else if(network.ifr_flags & IFF_RUNNING){
1072
struct timespec sleeptime = { .tv_nsec = 250000000 };
1073
ret = nanosleep(&sleeptime, NULL);
1074
if(ret == -1 and errno != EINTR){
1075
perror("nanosleep");
1078
ret = (int)TEMP_FAILURE_RETRY(close(sd));
1083
if(restore_loglevel){
1084
/* Restores kernel loglevel to default */
1085
ret = klogctl(7, NULL, 0);
1107
ret = init_gnutls_global(pubkey, seckey);
1109
fprintf(stderr, "init_gnutls_global failed\n");
1110
exitcode = EXIT_FAILURE;
1113
gnutls_initialized = true;
1116
if(mkdtemp(tempdir) == NULL){
1120
tempdir_created = true;
1122
if(not init_gpgme(pubkey, seckey, tempdir)){
1123
fprintf(stderr, "init_gpgme failed\n");
1124
exitcode = EXIT_FAILURE;
1127
gpgme_initialized = true;
1130
if(interface[0] != '\0'){
1131
if_index = (AvahiIfIndex) if_nametoindex(interface);
1133
fprintf(stderr, "No such interface: \"%s\"\n", interface);
1134
exitcode = EXIT_FAILURE;
1139
if(connect_to != NULL){
1140
/* Connect directly, do not use Zeroconf */
1141
/* (Mainly meant for debugging) */
1142
char *address = strrchr(connect_to, ':');
1143
if(address == NULL){
1144
fprintf(stderr, "No colon in address\n");
1145
exitcode = EXIT_FAILURE;
1149
ret = sscanf(address+1, "%" SCNdMAX "%n", &tmpmax, &numchars);
1150
if(ret < 1 or tmpmax != (uint16_t)tmpmax
1151
or address[numchars+1] != '\0'){
1152
fprintf(stderr, "Bad port number\n");
1153
exitcode = EXIT_FAILURE;
1156
port = (uint16_t)tmpmax;
1158
address = connect_to;
1159
/* Colon in address indicates IPv6 */
1161
if(strchr(address, ':') != NULL){
1166
ret = start_mandos_communication(address, port, if_index,
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;
1192
AvahiServerConfig config;
1193
/* Do not publish any local Zeroconf records */
783
returncode = EXIT_FAILURE;
790
avahi_set_log_function(empty_log);
793
/* Initialize the psuedo-RNG */
794
srand((unsigned int) time(NULL));
796
/* Allocate main loop object */
797
if (!(mc.simple_poll = avahi_simple_poll_new())) {
798
fprintf(stderr, "Failed to create simple poll object.\n");
799
returncode = EXIT_FAILURE;
803
/* Do not publish any local records */
1194
804
avahi_server_config_init(&config);
1195
805
config.publish_hinfo = 0;
1196
806
config.publish_addresses = 0;
1197
807
config.publish_workstation = 0;
1198
808
config.publish_domain = 0;
1200
810
/* Allocate a new server */
1201
mc.server = avahi_server_new(avahi_simple_poll_get
1202
(mc.simple_poll), &config, NULL,
811
mc.server=avahi_server_new(avahi_simple_poll_get(mc.simple_poll),
812
&config, NULL, NULL, &error);
1205
/* Free the Avahi configuration data */
814
/* Free the configuration data */
1206
815
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){
817
/* Check if creating the server object succeeded */
819
fprintf(stderr, "Failed to create server: %s\n",
820
avahi_strerror(error));
821
returncode = EXIT_FAILURE;
825
/* Create the service browser */
826
sb = avahi_s_service_browser_new(mc.server, if_index,
828
"_mandos._tcp", NULL, 0,
829
browse_callback, &mc);
831
fprintf(stderr, "Failed to create service browser: %s\n",
832
avahi_strerror(avahi_server_errno(mc.server)));
833
returncode = EXIT_FAILURE;
837
/* Run the main loop */
840
fprintf(stderr, "Starting avahi loop search\n");
843
avahi_simple_poll_loop(mc.simple_poll);
848
fprintf(stderr, "%s exiting\n", argv[0]);
853
avahi_s_service_browser_free(sb);
856
avahi_server_free(mc.server);
859
avahi_simple_poll_free(mc.simple_poll);