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"
74
static const char *certdir = "/conf/conf.d/mandos";
75
static const char *certfile = "openpgp-client.txt";
76
static const char *certkey = "openpgp-client-key.txt";
124
78
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 */
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){
402
262
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));
265
if ((ret = gnutls_global_init ())
266
!= GNUTLS_E_SUCCESS) {
267
fprintf (stderr, "global_init: %s\n", safer_gnutls_strerror(ret));
413
/* "Use a log level over 10 to enable all debugging options."
416
272
gnutls_global_set_log_level(11);
417
273
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();
276
/* openpgp credentials */
277
if ((ret = gnutls_certificate_allocate_credentials (&es->cred))
278
!= GNUTLS_E_SUCCESS) {
279
fprintf (stderr, "memory error: %s\n",
280
safer_gnutls_strerror(ret));
433
fprintf(stderr, "Attempting to use OpenPGP public key %s and"
434
" secret key %s as GnuTLS credentials\n", pubkeyfilename,
285
fprintf(stderr, "Attempting to use OpenPGP certificate %s"
286
" and keyfile %s as GnuTLS credentials\n", certfile,
438
290
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){
291
(es->cred, certfile, certkey, GNUTLS_OPENPGP_FMT_BASE64);
292
if (ret != GNUTLS_E_SUCCESS) {
294
(stderr, "Error[%d] while reading the OpenPGP key pair ('%s',"
296
ret, certfile, certkey);
297
fprintf(stdout, "The Error is: %s\n",
298
safer_gnutls_strerror(ret));
302
//GnuTLS server initialization
303
if ((ret = gnutls_dh_params_init (&es->dh_params))
304
!= GNUTLS_E_SUCCESS) {
305
fprintf (stderr, "Error in dh parameter initialization: %s\n",
306
safer_gnutls_strerror(ret));
310
if ((ret = gnutls_dh_params_generate2 (es->dh_params, DH_BITS))
311
!= GNUTLS_E_SUCCESS) {
312
fprintf (stderr, "Error in prime generation: %s\n",
313
safer_gnutls_strerror(ret));
317
gnutls_certificate_set_dh_params (es->cred, es->dh_params);
319
// GnuTLS session creation
320
if ((ret = gnutls_init (&es->session, GNUTLS_SERVER))
321
!= GNUTLS_E_SUCCESS){
481
322
fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
482
323
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);
326
if ((ret = gnutls_priority_set_direct (es->session, mc->priority, &err))
327
!= GNUTLS_E_SUCCESS) {
328
fprintf(stderr, "Syntax error at: %s\n", err);
329
fprintf(stderr, "GnuTLS error: %s\n",
330
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",
334
if ((ret = gnutls_credentials_set
335
(es->session, GNUTLS_CRD_CERTIFICATE, es->cred))
336
!= GNUTLS_E_SUCCESS) {
337
fprintf(stderr, "Error setting a credentials set: %s\n",
501
338
safer_gnutls_strerror(ret));
502
gnutls_deinit(*session);
506
342
/* ignore client certificate if any. */
507
gnutls_certificate_server_set_request(*session,
343
gnutls_certificate_server_set_request (es->session,
510
gnutls_dh_set_prime_bits(*session, mc.dh_bits);
346
gnutls_dh_set_prime_bits (es->session, DH_BITS);
515
/* Avahi log function callback */
516
351
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
517
352
__attribute__((unused)) const char *txt){}
519
/* Called when a Mandos server is found */
520
354
static int start_mandos_communication(const char *ip, uint16_t port,
521
355
AvahiIfIndex if_index,
526
struct sockaddr_in in;
527
struct sockaddr_in6 in6;
358
struct sockaddr_in6 to;
359
encrypted_session es;
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;
536
gnutls_session_t session;
537
int pf; /* Protocol family */
547
fprintf(stderr, "Bad address family: %d\n", af);
551
ret = init_gnutls_session(&session);
367
char interface[IF_NAMESIZE];
557
fprintf(stderr, "Setting up a TCP connection to %s, port %" PRIu16
370
fprintf(stderr, "Setting up a tcp connection to %s, port %d\n",
561
tcp_sd = socket(pf, SOCK_STREAM, 0);
374
tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
563
376
perror("socket");
381
if(if_indextoname((unsigned int)if_index, interface) == NULL){
383
perror("if_indextoname");
388
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);
391
memset(&to,0,sizeof(to)); /* Spurious warning */
392
to.sin6_family = AF_INET6;
393
ret = inet_pton(AF_INET6, ip, &to.sin6_addr);
576
395
perror("inet_pton");
580
399
fprintf(stderr, "Bad address: %s\n", ip);
584
to.in6.sin6_port = htons(port); /* Spurious warnings from
586
-Wunreachable-code */
588
if(IN6_IS_ADDR_LINKLOCAL /* Spurious warnings from */
589
(&to.in6.sin6_addr)){ /* -Wstrict-aliasing=2 or lower and
591
if(if_index == AVAHI_IF_UNSPEC){
592
fprintf(stderr, "An IPv6 link-local address is incomplete"
593
" without a network interface\n");
596
/* Set the network interface number as scope */
597
to.in6.sin6_scope_id = (uint32_t)if_index;
600
to.in.sin_port = htons(port); /* Spurious warnings from
602
-Wunreachable-code */
402
to.sin6_port = htons(port); /* Spurious warning */
404
to.sin6_scope_id = (uint32_t)if_index;
606
if(af == AF_INET6 and if_index != AVAHI_IF_UNSPEC){
607
char interface[IF_NAMESIZE];
608
if(if_indextoname((unsigned int)if_index, interface) == NULL){
609
perror("if_indextoname");
611
fprintf(stderr, "Connection to: %s%%%s, port %" PRIu16 "\n",
612
ip, interface, port);
615
fprintf(stderr, "Connection to: %s, port %" PRIu16 "\n", ip,
618
char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ?
619
INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = "";
622
pcret = inet_ntop(af, &(to.in6.sin6_addr), addrstr,
625
pcret = inet_ntop(af, &(to.in.sin_addr), addrstr,
631
if(strcmp(addrstr, ip) != 0){
632
fprintf(stderr, "Canonical address form: %s\n", addrstr);
407
fprintf(stderr, "Connection to: %s, port %d\n", ip, port);
408
/* char addrstr[INET6_ADDRSTRLEN]; */
409
/* if(inet_ntop(to.sin6_family, &(to.sin6_addr), addrstr, */
410
/* sizeof(addrstr)) == NULL){ */
411
/* perror("inet_ntop"); */
413
/* fprintf(stderr, "Really connecting to: %s, port %d\n", */
414
/* addrstr, ntohs(to.sin6_port)); */
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 (&es);
430
gnutls_transport_set_ptr (es.session,
431
(gnutls_transport_ptr_t) tcp_sd);
672
434
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){
437
ret = gnutls_handshake (es.session);
439
if (ret != GNUTLS_E_SUCCESS){
683
fprintf(stderr, "*** GnuTLS Handshake failed ***\n");
441
fprintf(stderr, "\n*** Handshake failed ***\n");
690
/* Read OpenPGP packet that contains the wanted password */
448
//Retrieve OpenPGP packet that contains the wanted password
693
fprintf(stderr, "Retrieving OpenPGP encrypted password from %s\n",
451
fprintf(stderr, "Retrieving pgp encrypted password from %s\n",
698
buffer_capacity = incbuffer(&buffer, buffer_length,
700
if(buffer_capacity == 0){
456
if (buffer_length + BUFFER_SIZE > buffer_capacity){
457
buffer = realloc(buffer, buffer_capacity + BUFFER_SIZE);
462
buffer_capacity += BUFFER_SIZE;
706
sret = gnutls_record_recv(session, buffer+buffer_length,
465
ret = gnutls_record_recv
466
(es.session, buffer+buffer_length, BUFFER_SIZE);
713
472
case GNUTLS_E_INTERRUPTED:
714
473
case GNUTLS_E_AGAIN:
716
475
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");
476
ret = gnutls_handshake (es.session);
478
fprintf(stderr, "\n*** Handshake failed ***\n");
728
485
fprintf(stderr, "Unknown error while reading data from"
729
" encrypted session with Mandos server\n");
486
" encrypted session with mandos server\n");
731
gnutls_bye(session, GNUTLS_SHUT_RDWR);
488
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
735
buffer_length += (size_t) sret;
492
buffer_length += (size_t) ret;
740
fprintf(stderr, "Closing TLS session\n");
743
gnutls_bye(session, GNUTLS_SHUT_RDWR);
745
if(buffer_length > 0){
496
if (buffer_length > 0){
746
497
decrypted_buffer_size = pgp_packet_decrypt(buffer,
749
if(decrypted_buffer_size >= 0){
501
if (decrypted_buffer_size >= 0){
751
502
while(written < (size_t) decrypted_buffer_size){
752
ret = (int)fwrite(decrypted_buffer + written, 1,
753
(size_t)decrypted_buffer_size - written,
503
ret = (int)fwrite (decrypted_buffer + written, 1,
504
(size_t)decrypted_buffer_size - written,
755
506
if(ret == 0 and ferror(stdout)){
757
508
fprintf(stderr, "Error writing encrypted data: %s\n",
814
567
char ip[AVAHI_ADDRESS_STR_MAX];
815
568
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);
570
fprintf(stderr, "Mandos server \"%s\" found on %s (%s) on"
571
" 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);
573
int ret = start_mandos_communication(ip, port, interface, mc);
828
579
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){
582
static void browse_callback( AvahiSServiceBrowser *b,
583
AvahiIfIndex interface,
584
AvahiProtocol protocol,
585
AvahiBrowserEvent event,
589
AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
591
mandos_context *mc = userdata;
592
assert(b); /* Spurious warning */
843
594
/* Called whenever a new services becomes available on the LAN or
844
595
is removed from the LAN */
848
599
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);
601
fprintf(stderr, "(Browser) %s\n",
602
avahi_strerror(avahi_server_errno(mc->server)));
603
avahi_simple_poll_quit(mc->simple_poll);
855
606
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
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)));
607
/* We ignore the returned resolver object. In the callback
608
function we free it. If the server is terminated before
609
the callback function is called the server will free
610
the resolver for us. */
612
if (!(avahi_s_service_resolver_new(mc->server, interface, protocol, name,
614
AVAHI_PROTO_INET6, 0,
615
resolve_callback, mc)))
616
fprintf(stderr, "Failed to resolve service '%s': %s\n", name,
617
avahi_strerror(avahi_server_errno(s)));
868
620
case AVAHI_BROWSER_REMOVE:
871
623
case AVAHI_BROWSER_ALL_FOR_NOW:
872
624
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);
629
/* Combines file name and path and returns the malloced new
630
string. some sane checks could/should be added */
631
static const char *combinepath(const char *first, const char *second){
632
size_t f_len = strlen(first);
633
size_t s_len = strlen(second);
634
char *tmp = malloc(f_len + s_len + 2);
639
memcpy(tmp, first, f_len);
643
memcpy(tmp + f_len + 1, second, s_len);
645
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",
650
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
651
AvahiServerConfig config;
652
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 = 2048, .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");
665
static struct option long_options[] = {
666
{"debug", no_argument, (int *)&debug, 1},
667
{"connect", required_argument, 0, 'C'},
668
{"interface", required_argument, 0, 'i'},
669
{"certdir", required_argument, 0, 'd'},
670
{"certkey", required_argument, 0, 'c'},
671
{"certfile", required_argument, 0, 'k'},
672
{"dh_bits", required_argument, 0, 'D'},
673
{"priority", required_argument, 0, 'p'},
676
int option_index = 0;
677
ret = getopt_long (argc, argv, "i:", long_options,
706
tmp = strtol(optarg, NULL, 10);
707
if (errno == ERANGE){
715
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__ */
722
certfile = combinepath(certdir, certfile);
723
if (certfile == NULL){
724
perror("combinepath");
725
returncode = EXIT_FAILURE;
729
certkey = combinepath(certdir, certkey);
730
if (certkey == NULL){
731
perror("combinepath");
732
returncode = EXIT_FAILURE;
736
if_index = (AvahiIfIndex) if_nametoindex(interface);
738
fprintf(stderr, "No such interface: \"%s\"\n", interface);
742
if(connect_to != NULL){
743
/* Connect directly, do not use Zeroconf */
744
/* (Mainly meant for debugging) */
745
char *address = strrchr(connect_to, ':');
747
fprintf(stderr, "No colon in address\n");
751
uint16_t port = (uint16_t) strtol(address+1, NULL, 10);
753
perror("Bad port number");
757
address = connect_to;
758
ret = start_mandos_communication(address, port, if_index);
1073
766
sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1075
768
perror("socket");
1076
exitcode = EXIT_FAILURE;
1078
if(restore_loglevel){
1079
ret = klogctl(7, NULL, 0);
1084
#endif /* __linux__ */
769
returncode = EXIT_FAILURE;
1087
strcpy(network.ifr_name, interface);
772
strcpy(network.ifr_name, interface);
1088
773
ret = ioctl(sd, SIOCGIFFLAGS, &network);
1090
776
perror("ioctl SIOCGIFFLAGS");
1092
if(restore_loglevel){
1093
ret = klogctl(7, NULL, 0);
1098
#endif /* __linux__ */
1099
exitcode = EXIT_FAILURE;
777
returncode = EXIT_FAILURE;
1102
780
if((network.ifr_flags & IFF_UP) == 0){
1103
781
network.ifr_flags |= IFF_UP;
1104
782
ret = ioctl(sd, SIOCSIFFLAGS, &network);
1106
784
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 */
785
returncode = EXIT_FAILURE;
792
avahi_set_log_function(empty_log);
795
/* Initialize the psuedo-RNG */
796
srand((unsigned int) time(NULL));
798
/* Allocate main loop object */
799
if (!(mc.simple_poll = avahi_simple_poll_new())) {
800
fprintf(stderr, "Failed to create simple poll object.\n");
801
returncode = EXIT_FAILURE;
805
/* Do not publish any local records */
1234
806
avahi_server_config_init(&config);
1235
807
config.publish_hinfo = 0;
1236
808
config.publish_addresses = 0;
1237
809
config.publish_workstation = 0;
1238
810
config.publish_domain = 0;
1240
812
/* Allocate a new server */
1241
mc.server = avahi_server_new(avahi_simple_poll_get
1242
(mc.simple_poll), &config, NULL,
1245
/* Free the Avahi configuration data */
813
mc.server = avahi_server_new(avahi_simple_poll_get(simple_poll),
814
&config, NULL, NULL, &error);
816
/* Free the configuration data */
1246
817
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){
819
/* Check if creating the server object succeeded */
821
fprintf(stderr, "Failed to create server: %s\n",
822
avahi_strerror(error));
823
returncode = EXIT_FAILURE;
827
/* Create the service browser */
828
sb = avahi_s_service_browser_new(mc.server, if_index,
830
"_mandos._tcp", NULL, 0,
831
browse_callback, &mc);
833
fprintf(stderr, "Failed to create service browser: %s\n",
834
avahi_strerror(avahi_server_errno(mc.server)));
835
returncode = EXIT_FAILURE;
839
/* Run the main loop */
842
fprintf(stderr, "Starting avahi loop search\n");
845
avahi_simple_poll_loop(simple_poll);
850
fprintf(stderr, "%s exiting\n", argv[0]);
855
avahi_s_service_browser_free(sb);
858
avahi_server_free(mc.server);
861
avahi_simple_poll_free(simple_poll);