32
31
/* Needed by GPGME, specifically gpgme_data_seek() */
33
#ifndef _LARGEFILE_SOURCE
34
32
#define _LARGEFILE_SOURCE
36
#ifndef _FILE_OFFSET_BITS
37
33
#define _FILE_OFFSET_BITS 64
40
#define _GNU_SOURCE /* TEMP_FAILURE_RETRY(), asprintf() */
42
#include <stdio.h> /* fprintf(), stderr, fwrite(),
43
stdout, ferror(), remove() */
44
#include <stdint.h> /* uint16_t, uint32_t */
45
#include <stddef.h> /* NULL, size_t, ssize_t */
46
#include <stdlib.h> /* free(), EXIT_SUCCESS, EXIT_FAILURE,
48
#include <stdbool.h> /* bool, false, true */
49
#include <string.h> /* memset(), strcmp(), strlen(),
50
strerror(), asprintf(), strcpy() */
51
#include <sys/ioctl.h> /* ioctl */
52
#include <sys/types.h> /* socket(), inet_pton(), sockaddr,
53
sockaddr_in6, PF_INET6,
54
SOCK_STREAM, uid_t, gid_t, open(),
56
#include <sys/stat.h> /* open() */
57
#include <sys/socket.h> /* socket(), struct sockaddr_in6,
58
inet_pton(), connect() */
59
#include <fcntl.h> /* open() */
60
#include <dirent.h> /* opendir(), struct dirent, readdir()
62
#include <inttypes.h> /* PRIu16, PRIdMAX, intmax_t,
64
#include <assert.h> /* assert() */
65
#include <errno.h> /* perror(), errno */
66
#include <time.h> /* nanosleep(), time() */
67
#include <net/if.h> /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
68
SIOCSIFFLAGS, if_indextoname(),
69
if_nametoindex(), IF_NAMESIZE */
70
#include <netinet/in.h> /* IN6_IS_ADDR_LINKLOCAL,
71
INET_ADDRSTRLEN, INET6_ADDRSTRLEN
73
#include <unistd.h> /* close(), SEEK_SET, off_t, write(),
74
getuid(), getgid(), setuid(),
76
#include <arpa/inet.h> /* inet_pton(), htons */
77
#include <iso646.h> /* not, or, and */
78
#include <argp.h> /* struct argp_option, error_t, struct
79
argp_state, struct argp,
80
argp_parse(), ARGP_KEY_ARG,
81
ARGP_KEY_END, ARGP_ERR_UNKNOWN */
82
#include <signal.h> /* sigemptyset(), sigaddset(),
83
sigaction(), SIGTERM, sigaction,
87
#include <sys/klog.h> /* klogctl() */
88
#endif /* __linux__ */
91
/* 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
94
43
#include <avahi-core/core.h>
95
44
#include <avahi-core/lookup.h>
96
45
#include <avahi-core/log.h>
98
47
#include <avahi-common/malloc.h>
99
48
#include <avahi-common/error.h>
102
#include <gnutls/gnutls.h> /* All GnuTLS types, constants and
105
init_gnutls_session(),
107
#include <gnutls/openpgp.h>
108
/* gnutls_certificate_set_openpgp_key_file(),
109
GNUTLS_OPENPGP_FMT_BASE64 */
112
#include <gpgme.h> /* All GPGME types, constants and
115
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() */
118
71
#define BUFFER_SIZE 256
120
#define PATHDIR "/conf/conf.d/mandos"
121
#define SECKEY "seckey.txt"
122
#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";
124
77
bool debug = false;
125
static const char mandos_protocol_version[] = "1";
126
const char *argp_program_version = "mandos-client " VERSION;
127
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
129
/* Used for passing in values through the Avahi callback functions */
79
/* Used for passing in values through all the callback functions */
131
81
AvahiSimplePoll *simple_poll;
132
82
AvahiServer *server;
133
83
gnutls_certificate_credentials_t cred;
134
84
unsigned int dh_bits;
135
gnutls_dh_params_t dh_params;
136
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;
140
/* global context so signal handler can reach it*/
141
mandos_context mc = { .simple_poll = NULL, .server = NULL,
142
.dh_bits = 1024, .priority = "SECURE256"
143
":!CTYPE-X.509:+CTYPE-OPENPGP" };
146
* Make additional room in "buffer" for at least BUFFER_SIZE more
147
* bytes. "buffer_capacity" is how much is currently allocated,
148
* "buffer_length" is how much is already used.
150
size_t incbuffer(char **buffer, size_t buffer_length,
151
size_t buffer_capacity){
152
if(buffer_length + BUFFER_SIZE > buffer_capacity){
153
*buffer = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
157
buffer_capacity += BUFFER_SIZE;
159
return buffer_capacity;
165
static bool init_gpgme(const char *seckey,
166
const char *pubkey, const char *tempdir){
95
ssize_t new_packet_capacity = 0;
96
ssize_t new_packet_length = 0;
169
97
gpgme_engine_info_t engine_info;
173
* Helper function to insert pub and seckey to the engine keyring.
175
bool import_key(const char *filename){
177
gpgme_data_t pgp_data;
179
fd = (int)TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
185
rc = gpgme_data_new_from_fd(&pgp_data, fd);
186
if(rc != GPG_ERR_NO_ERROR){
187
fprintf(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
188
gpgme_strsource(rc), gpgme_strerror(rc));
192
rc = gpgme_op_import(mc.ctx, pgp_data);
193
if(rc != GPG_ERR_NO_ERROR){
194
fprintf(stderr, "bad gpgme_op_import: %s: %s\n",
195
gpgme_strsource(rc), gpgme_strerror(rc));
199
ret = (int)TEMP_FAILURE_RETRY(close(fd));
203
gpgme_data_release(pgp_data);
208
fprintf(stderr, "Initializing GPGME\n");
100
fprintf(stderr, "Trying to decrypt OpenPGP packet\n");
212
104
gpgme_check_version(NULL);
213
105
rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
214
if(rc != GPG_ERR_NO_ERROR){
106
if (rc != GPG_ERR_NO_ERROR){
215
107
fprintf(stderr, "bad gpgme_engine_check_version: %s: %s\n",
216
108
gpgme_strsource(rc), gpgme_strerror(rc));
220
/* Set GPGME home directory for the OpenPGP engine only */
221
rc = gpgme_get_engine_info(&engine_info);
222
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){
223
115
fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
224
116
gpgme_strsource(rc), gpgme_strerror(rc));
227
119
while(engine_info != NULL){
228
120
if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
229
121
gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
230
engine_info->file_name, tempdir);
122
engine_info->file_name, homedir);
233
125
engine_info = engine_info->next;
235
127
if(engine_info == NULL){
236
fprintf(stderr, "Could not set GPGME home dir to %s\n", tempdir);
240
/* Create new GPGME "context" */
241
rc = gpgme_new(&(mc.ctx));
242
if(rc != GPG_ERR_NO_ERROR){
243
fprintf(stderr, "bad gpgme_new: %s: %s\n",
244
gpgme_strsource(rc), gpgme_strerror(rc));
248
if(not import_key(pubkey) or not import_key(seckey)){
256
* Decrypt OpenPGP data.
257
* Returns -1 on error
259
static ssize_t pgp_packet_decrypt(const char *cryptotext,
262
gpgme_data_t dh_crypto, dh_plain;
265
size_t plaintext_capacity = 0;
266
ssize_t plaintext_length = 0;
269
fprintf(stderr, "Trying to decrypt OpenPGP data\n");
272
/* Create new GPGME data buffer from memory cryptotext */
273
rc = gpgme_data_new_from_mem(&dh_crypto, cryptotext, crypto_size,
275
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){
276
135
fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
277
136
gpgme_strsource(rc), gpgme_strerror(rc));
281
140
/* Create new empty GPGME data buffer for the plaintext */
282
141
rc = gpgme_data_new(&dh_plain);
283
if(rc != GPG_ERR_NO_ERROR){
142
if (rc != GPG_ERR_NO_ERROR){
284
143
fprintf(stderr, "bad gpgme_data_new: %s: %s\n",
285
144
gpgme_strsource(rc), gpgme_strerror(rc));
286
gpgme_data_release(dh_crypto);
290
/* Decrypt data from the cryptotext data buffer to the plaintext
292
rc = gpgme_op_decrypt(mc.ctx, dh_crypto, dh_plain);
293
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){
294
160
fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
295
161
gpgme_strsource(rc), gpgme_strerror(rc));
296
plaintext_length = -1;
298
gpgme_decrypt_result_t result;
299
result = gpgme_op_decrypt_result(mc.ctx);
301
fprintf(stderr, "gpgme_op_decrypt_result failed\n");
303
fprintf(stderr, "Unsupported algorithm: %s\n",
304
result->unsupported_algorithm);
305
fprintf(stderr, "Wrong key usage: %u\n",
306
result->wrong_key_usage);
307
if(result->file_name != NULL){
308
fprintf(stderr, "File name: %s\n", result->file_name);
310
gpgme_recipient_t recipient;
311
recipient = result->recipients;
313
while(recipient != NULL){
314
fprintf(stderr, "Public key algorithm: %s\n",
315
gpgme_pubkey_algo_name(recipient->pubkey_algo));
316
fprintf(stderr, "Key ID: %s\n", recipient->keyid);
317
fprintf(stderr, "Secret key available: %s\n",
318
recipient->status == GPG_ERR_NO_SECKEY
320
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;
329
fprintf(stderr, "Decryption of OpenPGP data succeeded\n");
198
/* Delete the GPGME FILE pointer cryptotext data buffer */
199
gpgme_data_release(dh_crypto);
332
201
/* Seek back to the beginning of the GPGME plaintext data buffer */
333
if(gpgme_data_seek(dh_plain, (off_t)0, SEEK_SET) == -1){
334
perror("gpgme_data_seek");
335
plaintext_length = -1;
202
if (gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET) == -1){
203
perror("pgpme_data_seek");
341
plaintext_capacity = incbuffer(plaintext,
342
(size_t)plaintext_length,
344
if(plaintext_capacity == 0){
346
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;
350
ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
219
ret = gpgme_data_read(dh_plain, *new_packet + new_packet_length,
352
221
/* Print the data, if any */
358
226
perror("gpgme_data_read");
359
plaintext_length = -1;
362
plaintext_length += ret;
366
fprintf(stderr, "Decrypted password is: ");
367
for(ssize_t i = 0; i < plaintext_length; i++){
368
fprintf(stderr, "%02hhX ", (*plaintext)[i]);
370
fprintf(stderr, "\n");
375
/* Delete the GPGME cryptotext data buffer */
376
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"); */
378
240
/* Delete the GPGME plaintext data buffer */
379
241
gpgme_data_release(dh_plain);
380
return plaintext_length;
242
return new_packet_length;
383
static const char * safer_gnutls_strerror(int value){
384
const char *ret = gnutls_strerror(value); /* Spurious warning from
385
-Wunreachable-code */
245
static const char * safer_gnutls_strerror (int value) {
246
const char *ret = gnutls_strerror (value);
387
248
ret = "(unknown)";
391
/* GnuTLS log function callback */
392
252
static void debuggnutls(__attribute__((unused)) int level,
393
253
const char* string){
394
fprintf(stderr, "GnuTLS: %s", string);
254
fprintf(stderr, "%s", string);
397
static int init_gnutls_global(const char *pubkeyfilename,
398
const char *seckeyfilename){
257
static int initgnutls(mandos_context *mc, gnutls_session_t *session,
258
gnutls_dh_params_t *dh_params){
402
263
fprintf(stderr, "Initializing GnuTLS\n");
405
ret = gnutls_global_init();
406
if(ret != GNUTLS_E_SUCCESS){
407
fprintf(stderr, "GnuTLS global_init: %s\n",
408
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));
413
/* "Use a log level over 10 to enable all debugging options."
416
273
gnutls_global_set_log_level(11);
417
274
gnutls_global_set_log_function(debuggnutls);
420
/* OpenPGP credentials */
421
gnutls_certificate_allocate_credentials(&mc.cred);
422
if(ret != GNUTLS_E_SUCCESS){
423
fprintf(stderr, "GnuTLS memory error: %s\n", /* Spurious warning
427
safer_gnutls_strerror(ret));
428
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));
433
fprintf(stderr, "Attempting to use OpenPGP public key %s and"
434
" 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,
438
291
ret = gnutls_certificate_set_openpgp_key_file
439
(mc.cred, pubkeyfilename, seckeyfilename,
440
GNUTLS_OPENPGP_FMT_BASE64);
441
if(ret != GNUTLS_E_SUCCESS){
443
"Error[%d] while reading the OpenPGP key pair ('%s',"
444
" '%s')\n", ret, pubkeyfilename, seckeyfilename);
445
fprintf(stderr, "The GnuTLS error is: %s\n",
446
safer_gnutls_strerror(ret));
450
/* GnuTLS server initialization */
451
ret = gnutls_dh_params_init(&mc.dh_params);
452
if(ret != GNUTLS_E_SUCCESS){
453
fprintf(stderr, "Error in GnuTLS DH parameter initialization:"
454
" %s\n", safer_gnutls_strerror(ret));
457
ret = gnutls_dh_params_generate2(mc.dh_params, mc.dh_bits);
458
if(ret != GNUTLS_E_SUCCESS){
459
fprintf(stderr, "Error in GnuTLS prime generation: %s\n",
460
safer_gnutls_strerror(ret));
464
gnutls_certificate_set_dh_params(mc.cred, mc.dh_params);
470
gnutls_certificate_free_credentials(mc.cred);
471
gnutls_global_deinit();
472
gnutls_dh_params_deinit(mc.dh_params);
476
static int init_gnutls_session(gnutls_session_t *session){
478
/* GnuTLS session creation */
479
ret = gnutls_init(session, GNUTLS_SERVER);
480
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){
481
323
fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
482
324
safer_gnutls_strerror(ret));
487
ret = gnutls_priority_set_direct(*session, mc.priority, &err);
488
if(ret != GNUTLS_E_SUCCESS){
489
fprintf(stderr, "Syntax error at: %s\n", err);
490
fprintf(stderr, "GnuTLS error: %s\n",
491
safer_gnutls_strerror(ret));
492
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));
497
ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
499
if(ret != GNUTLS_E_SUCCESS){
500
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",
501
339
safer_gnutls_strerror(ret));
502
gnutls_deinit(*session);
506
343
/* ignore client certificate if any. */
507
gnutls_certificate_server_set_request(*session,
344
gnutls_certificate_server_set_request (*session,
510
gnutls_dh_set_prime_bits(*session, mc.dh_bits);
347
gnutls_dh_set_prime_bits (*session, mc->dh_bits);
515
/* Avahi log function callback */
516
352
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
517
353
__attribute__((unused)) const char *txt){}
519
/* Called when a Mandos server is found */
520
355
static int start_mandos_communication(const char *ip, uint16_t port,
521
356
AvahiIfIndex if_index,
526
struct sockaddr_in in;
527
struct sockaddr_in6 in6;
359
struct sockaddr_in6 to;
529
360
char *buffer = NULL;
530
361
char *decrypted_buffer;
531
362
size_t buffer_length = 0;
532
363
size_t buffer_capacity = 0;
533
364
ssize_t decrypted_buffer_size;
367
char interface[IF_NAMESIZE];
536
368
gnutls_session_t session;
537
int pf; /* Protocol family */
547
fprintf(stderr, "Bad address family: %d\n", af);
551
ret = init_gnutls_session(&session);
369
gnutls_dh_params_t dh_params;
557
fprintf(stderr, "Setting up a TCP connection to %s, port %" PRIu16
372
fprintf(stderr, "Setting up a tcp connection to %s, port %d\n",
561
tcp_sd = socket(pf, SOCK_STREAM, 0);
376
tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
563
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);
567
memset(&to, 0, sizeof(to));
569
to.in6.sin6_family = (uint16_t)af;
570
ret = inet_pton(af, ip, &to.in6.sin6_addr);
572
to.in.sin_family = (sa_family_t)af;
573
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);
576
394
perror("inet_pton");
638
ret = connect(tcp_sd, &to.in6, sizeof(to));
640
ret = connect(tcp_sd, &to.in, sizeof(to)); /* IPv4 */
418
ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
643
420
perror("connect");
647
const char *out = mandos_protocol_version;
650
size_t out_size = strlen(out);
651
ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
652
out_size - written));
658
written += (size_t)ret;
659
if(written < out_size){
662
if(out == mandos_protocol_version){
424
ret = initgnutls (mc, &session, &dh_params);
430
gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t) tcp_sd);
672
433
fprintf(stderr, "Establishing TLS session with %s\n", ip);
675
gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t) tcp_sd);
678
ret = gnutls_handshake(session);
679
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
681
if(ret != GNUTLS_E_SUCCESS){
436
ret = gnutls_handshake (session);
438
if (ret != GNUTLS_E_SUCCESS){
683
fprintf(stderr, "*** GnuTLS Handshake failed ***\n");
440
fprintf(stderr, "\n*** Handshake failed ***\n");
690
/* Read OpenPGP packet that contains the wanted password */
447
//Retrieve OpenPGP packet that contains the wanted password
693
fprintf(stderr, "Retrieving OpenPGP encrypted password from %s\n",
450
fprintf(stderr, "Retrieving pgp encrypted password from %s\n",
698
buffer_capacity = incbuffer(&buffer, buffer_length,
700
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;
706
sret = gnutls_record_recv(session, buffer+buffer_length,
464
ret = gnutls_record_recv(session, buffer+buffer_length,
713
471
case GNUTLS_E_INTERRUPTED:
714
472
case GNUTLS_E_AGAIN:
716
474
case GNUTLS_E_REHANDSHAKE:
718
ret = gnutls_handshake(session);
719
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
721
fprintf(stderr, "*** GnuTLS Re-handshake failed ***\n");
475
ret = gnutls_handshake (session);
477
fprintf(stderr, "\n*** Handshake failed ***\n");
728
484
fprintf(stderr, "Unknown error while reading data from"
729
" encrypted session with Mandos server\n");
485
" encrypted session with mandos server\n");
731
gnutls_bye(session, GNUTLS_SHUT_RDWR);
487
gnutls_bye (session, GNUTLS_SHUT_RDWR);
735
buffer_length += (size_t) sret;
491
buffer_length += (size_t) ret;
740
fprintf(stderr, "Closing TLS session\n");
743
gnutls_bye(session, GNUTLS_SHUT_RDWR);
745
if(buffer_length > 0){
495
if (buffer_length > 0){
746
496
decrypted_buffer_size = pgp_packet_decrypt(buffer,
749
if(decrypted_buffer_size >= 0){
500
if (decrypted_buffer_size >= 0){
751
501
while(written < (size_t) decrypted_buffer_size){
752
ret = (int)fwrite(decrypted_buffer + written, 1,
753
(size_t)decrypted_buffer_size - written,
502
ret = (int)fwrite (decrypted_buffer + written, 1,
503
(size_t)decrypted_buffer_size - written,
755
505
if(ret == 0 and ferror(stdout)){
757
507
fprintf(stderr, "Error writing encrypted data: %s\n",
814
566
char ip[AVAHI_ADDRESS_STR_MAX];
815
567
avahi_address_snprint(ip, sizeof(ip), address);
817
fprintf(stderr, "Mandos server \"%s\" found on %s (%s, %"
818
PRIdMAX ") on port %" PRIu16 "\n", name, host_name,
819
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);
821
int ret = start_mandos_communication(ip, port, interface,
822
avahi_proto_to_af(proto));
824
avahi_simple_poll_quit(mc.simple_poll);
572
int ret = start_mandos_communication(ip, port, interface, mc);
828
578
avahi_s_service_resolver_free(r);
831
static void browse_callback(AvahiSServiceBrowser *b,
832
AvahiIfIndex interface,
833
AvahiProtocol protocol,
834
AvahiBrowserEvent event,
838
AVAHI_GCC_UNUSED AvahiLookupResultFlags
840
AVAHI_GCC_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 */
843
593
/* Called whenever a new services becomes available on the LAN or
844
594
is removed from the LAN */
848
598
case AVAHI_BROWSER_FAILURE:
850
fprintf(stderr, "(Avahi browser) %s\n",
851
avahi_strerror(avahi_server_errno(mc.server)));
852
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);
855
605
case AVAHI_BROWSER_NEW:
856
/* We ignore the returned Avahi resolver object. In the callback
857
function we free it. If the Avahi server is terminated before
858
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. */
861
if(avahi_s_service_resolver_new(mc.server, interface, protocol,
862
name, type, domain, protocol, 0,
863
resolve_callback, NULL) == NULL)
864
fprintf(stderr, "Avahi: Failed to resolve service '%s': %s\n",
865
name, avahi_strerror(avahi_server_errno(mc.server)));
611
if (!(avahi_s_service_resolver_new(mc->server, interface, protocol, name,
613
AVAHI_PROTO_INET6, 0,
614
resolve_callback, mc)))
615
fprintf(stderr, "Failed to resolve service '%s': %s\n", name,
616
avahi_strerror(avahi_server_errno(mc->server)));
868
619
case AVAHI_BROWSER_REMOVE:
871
622
case AVAHI_BROWSER_ALL_FOR_NOW:
872
623
case AVAHI_BROWSER_CACHE_EXHAUSTED:
874
fprintf(stderr, "No Mandos server found, still searching...\n");
880
sig_atomic_t quit_now = 0;
882
/* stop main loop after sigterm has been called */
883
static void handle_sigterm(__attribute__((unused)) int sig){
888
int old_errno = errno;
889
if(mc.simple_poll != NULL){
890
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';
895
int main(int argc, char *argv[]){
896
AvahiSServiceBrowser *sb = NULL;
901
int exitcode = EXIT_SUCCESS;
902
const char *interface = "eth0";
903
struct ifreq network;
907
char *connect_to = NULL;
908
char tempdir[] = "/tmp/mandosXXXXXX";
909
bool tempdir_created = false;
910
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
911
const char *seckey = PATHDIR "/" SECKEY;
912
const char *pubkey = PATHDIR "/" PUBKEY;
914
bool gnutls_initialized = false;
915
bool gpgme_initialized = false;
918
struct sigaction old_sigterm_action;
919
struct sigaction sigterm_action = { .sa_handler = handle_sigterm };
922
struct argp_option options[] = {
923
{ .name = "debug", .key = 128,
924
.doc = "Debug mode", .group = 3 },
925
{ .name = "connect", .key = 'c',
926
.arg = "ADDRESS:PORT",
927
.doc = "Connect directly to a specific Mandos server",
929
{ .name = "interface", .key = 'i',
931
.doc = "Network interface that will be used to search for"
934
{ .name = "seckey", .key = 's',
936
.doc = "OpenPGP secret key file base name",
938
{ .name = "pubkey", .key = 'p',
940
.doc = "OpenPGP public key file base name",
942
{ .name = "dh-bits", .key = 129,
944
.doc = "Bit length of the prime number used in the"
945
" Diffie-Hellman key exchange",
947
{ .name = "priority", .key = 130,
949
.doc = "GnuTLS priority string for the TLS handshake",
951
{ .name = "delay", .key = 131,
953
.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"};
958
error_t parse_opt(int key, char *arg,
959
struct argp_state *state){
961
case 128: /* --debug */
964
case 'c': /* --connect */
967
case 'i': /* --interface */
970
case 's': /* --seckey */
973
case 'p': /* --pubkey */
976
case 129: /* --dh-bits */
978
tmpmax = strtoimax(arg, &tmp, 10);
979
if(errno != 0 or tmp == arg or *tmp != '\0'
980
or tmpmax != (typeof(mc.dh_bits))tmpmax){
981
fprintf(stderr, "Bad number of DH bits\n");
984
mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
986
case 130: /* --priority */
989
case 131: /* --delay */
991
delay = strtof(arg, &tmp);
992
if(errno != 0 or tmp == arg or *tmp != '\0'){
993
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;
1002
return ARGP_ERR_UNKNOWN;
1007
struct argp argp = { .options = options, .parser = parse_opt,
1009
.doc = "Mandos client -- Get and decrypt"
1010
" passwords from a Mandos server" };
1011
ret = argp_parse(&argp, argc, argv, 0, 0, NULL);
1012
if(ret == ARGP_ERR_UNKNOWN){
1013
fprintf(stderr, "Unknown error while parsing arguments\n");
1014
exitcode = EXIT_FAILURE;
1020
avahi_set_log_function(empty_log);
1023
/* Initialize Avahi early so avahi_simple_poll_quit() can be called
1024
from the signal handler */
1025
/* Initialize the pseudo-RNG for Avahi */
1026
srand((unsigned int) time(NULL));
1027
mc.simple_poll = avahi_simple_poll_new();
1028
if(mc.simple_poll == NULL){
1029
fprintf(stderr, "Avahi: Failed to create simple poll object.\n");
1030
exitcode = EXIT_FAILURE;
1034
sigemptyset(&sigterm_action.sa_mask);
1035
ret = sigaddset(&sigterm_action.sa_mask, SIGINT);
1037
perror("sigaddset");
1038
exitcode = EXIT_FAILURE;
1041
ret = sigaddset(&sigterm_action.sa_mask, SIGHUP);
1043
perror("sigaddset");
1044
exitcode = EXIT_FAILURE;
1047
ret = sigaddset(&sigterm_action.sa_mask, SIGTERM);
1049
perror("sigaddset");
1050
exitcode = EXIT_FAILURE;
1053
ret = sigaction(SIGTERM, &sigterm_action, &old_sigterm_action);
1055
perror("sigaction");
1056
exitcode = EXIT_FAILURE;
1060
/* If the interface is down, bring it up */
1061
if(interface[0] != '\0'){
1063
/* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
1064
messages to mess up the prompt */
1065
ret = klogctl(8, NULL, 5);
1066
bool restore_loglevel = true;
1068
restore_loglevel = false;
1071
#endif /* __linux__ */
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);
1073
764
sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1075
766
perror("socket");
1076
exitcode = EXIT_FAILURE;
1078
if(restore_loglevel){
1079
ret = klogctl(7, NULL, 0);
1084
#endif /* __linux__ */
767
returncode = EXIT_FAILURE;
1087
strcpy(network.ifr_name, interface);
770
strcpy(network.ifr_name, interface); /* Spurious warning */
1088
771
ret = ioctl(sd, SIOCGIFFLAGS, &network);
1090
774
perror("ioctl SIOCGIFFLAGS");
1092
if(restore_loglevel){
1093
ret = klogctl(7, NULL, 0);
1098
#endif /* __linux__ */
1099
exitcode = EXIT_FAILURE;
775
returncode = EXIT_FAILURE;
1102
778
if((network.ifr_flags & IFF_UP) == 0){
1103
779
network.ifr_flags |= IFF_UP;
1104
780
ret = ioctl(sd, SIOCSIFFLAGS, &network);
1106
782
perror("ioctl SIOCSIFFLAGS");
1107
exitcode = EXIT_FAILURE;
1109
if(restore_loglevel){
1110
ret = klogctl(7, NULL, 0);
1115
#endif /* __linux__ */
1119
/* sleep checking until interface is running */
1120
for(int i=0; i < delay * 4; i++){
1121
ret = ioctl(sd, SIOCGIFFLAGS, &network);
1123
perror("ioctl SIOCGIFFLAGS");
1124
} else if(network.ifr_flags & IFF_RUNNING){
1127
struct timespec sleeptime = { .tv_nsec = 250000000 };
1128
ret = nanosleep(&sleeptime, NULL);
1129
if(ret == -1 and errno != EINTR){
1130
perror("nanosleep");
1133
ret = (int)TEMP_FAILURE_RETRY(close(sd));
1138
if(restore_loglevel){
1139
/* Restores kernel loglevel to default */
1140
ret = klogctl(7, NULL, 0);
1145
#endif /* __linux__ */
1162
ret = init_gnutls_global(pubkey, seckey);
1164
fprintf(stderr, "init_gnutls_global failed\n");
1165
exitcode = EXIT_FAILURE;
1168
gnutls_initialized = true;
1171
if(mkdtemp(tempdir) == NULL){
1175
tempdir_created = true;
1177
if(not init_gpgme(pubkey, seckey, tempdir)){
1178
fprintf(stderr, "init_gpgme failed\n");
1179
exitcode = EXIT_FAILURE;
1182
gpgme_initialized = true;
1185
if(interface[0] != '\0'){
1186
if_index = (AvahiIfIndex) if_nametoindex(interface);
1188
fprintf(stderr, "No such interface: \"%s\"\n", interface);
1189
exitcode = EXIT_FAILURE;
1194
if(connect_to != NULL){
1195
/* Connect directly, do not use Zeroconf */
1196
/* (Mainly meant for debugging) */
1197
char *address = strrchr(connect_to, ':');
1198
if(address == NULL){
1199
fprintf(stderr, "No colon in address\n");
1200
exitcode = EXIT_FAILURE;
1205
tmpmax = strtoimax(address+1, &tmp, 10);
1206
if(errno != 0 or tmp == address+1 or *tmp != '\0'
1207
or tmpmax != (uint16_t)tmpmax){
1208
fprintf(stderr, "Bad port number\n");
1209
exitcode = EXIT_FAILURE;
1212
port = (uint16_t)tmpmax;
1214
address = connect_to;
1215
/* Colon in address indicates IPv6 */
1217
if(strchr(address, ':') != NULL){
1222
ret = start_mandos_communication(address, port, if_index, af);
1224
exitcode = EXIT_FAILURE;
1226
exitcode = EXIT_SUCCESS;
1232
AvahiServerConfig config;
1233
/* 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 */
1234
804
avahi_server_config_init(&config);
1235
805
config.publish_hinfo = 0;
1236
806
config.publish_addresses = 0;
1237
807
config.publish_workstation = 0;
1238
808
config.publish_domain = 0;
1240
810
/* Allocate a new server */
1241
mc.server = avahi_server_new(avahi_simple_poll_get
1242
(mc.simple_poll), &config, NULL,
811
mc.server=avahi_server_new(avahi_simple_poll_get(mc.simple_poll),
812
&config, NULL, NULL, &error);
1245
/* Free the Avahi configuration data */
814
/* Free the configuration data */
1246
815
avahi_server_config_free(&config);
1249
/* Check if creating the Avahi server object succeeded */
1250
if(mc.server == NULL){
1251
fprintf(stderr, "Failed to create Avahi server: %s\n",
1252
avahi_strerror(error));
1253
exitcode = EXIT_FAILURE;
1257
/* Create the Avahi service browser */
1258
sb = avahi_s_service_browser_new(mc.server, if_index,
1259
AVAHI_PROTO_UNSPEC, "_mandos._tcp",
1260
NULL, 0, browse_callback, NULL);
1262
fprintf(stderr, "Failed to create service browser: %s\n",
1263
avahi_strerror(avahi_server_errno(mc.server)));
1264
exitcode = EXIT_FAILURE;
1268
/* Run the main loop */
1271
fprintf(stderr, "Starting Avahi loop search\n");
1274
avahi_simple_poll_loop(mc.simple_poll);
1279
fprintf(stderr, "%s exiting\n", argv[0]);
1282
/* Cleanup things */
1284
avahi_s_service_browser_free(sb);
1286
if(mc.server != NULL)
1287
avahi_server_free(mc.server);
1289
if(mc.simple_poll != NULL)
1290
avahi_simple_poll_free(mc.simple_poll);
1292
if(gnutls_initialized){
1293
gnutls_certificate_free_credentials(mc.cred);
1294
gnutls_global_deinit();
1295
gnutls_dh_params_deinit(mc.dh_params);
1298
if(gpgme_initialized){
1299
gpgme_release(mc.ctx);
1302
/* Removes the temp directory used by GPGME */
1303
if(tempdir_created){
1305
struct dirent *direntry;
1306
d = opendir(tempdir);
1308
if(errno != ENOENT){
1313
direntry = readdir(d);
1314
if(direntry == NULL){
1317
/* Skip "." and ".." */
1318
if(direntry->d_name[0] == '.'
1319
and (direntry->d_name[1] == '\0'
1320
or (direntry->d_name[1] == '.'
1321
and direntry->d_name[2] == '\0'))){
1324
char *fullname = NULL;
1325
ret = asprintf(&fullname, "%s/%s", tempdir,
1331
ret = remove(fullname);
1333
fprintf(stderr, "remove(\"%s\"): %s\n", fullname,
1340
ret = rmdir(tempdir);
1341
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);