25
26
* along with this program. If not, see
26
27
* <http://www.gnu.org/licenses/>.
28
* Contact the authors at <https://www.fukt.bsnet.se/~belorn/> and
29
* <https://www.fukt.bsnet.se/~teddy/>.
29
* Contact the authors at <mandos@fukt.bsnet.se>.
32
#define _FORTIFY_SOURCE 2
32
/* Needed by GPGME, specifically gpgme_data_seek() */
34
33
#define _LARGEFILE_SOURCE
35
34
#define _FILE_OFFSET_BITS 64
41
#include <net/if.h> /* if_nametoindex */
36
#define _GNU_SOURCE /* TEMP_FAILURE_RETRY(), asprintf() */
38
#include <stdio.h> /* fprintf(), stderr, fwrite(),
39
stdout, ferror(), remove() */
40
#include <stdint.h> /* uint16_t, uint32_t */
41
#include <stddef.h> /* NULL, size_t, ssize_t */
42
#include <stdlib.h> /* free(), EXIT_SUCCESS, EXIT_FAILURE,
44
#include <stdbool.h> /* bool, false, true */
45
#include <string.h> /* memset(), strcmp(), strlen(),
46
strerror(), asprintf(), strcpy() */
47
#include <sys/ioctl.h> /* ioctl */
48
#include <sys/types.h> /* socket(), inet_pton(), sockaddr,
49
sockaddr_in6, PF_INET6,
50
SOCK_STREAM, uid_t, gid_t, open(),
52
#include <sys/stat.h> /* open() */
53
#include <sys/socket.h> /* socket(), struct sockaddr_in6,
54
inet_pton(), connect() */
55
#include <fcntl.h> /* open() */
56
#include <dirent.h> /* opendir(), struct dirent, readdir()
58
#include <inttypes.h> /* PRIu16, PRIdMAX, intmax_t,
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(),
79
sigaction(), SIGTERM, sigaction,
83
#include <sys/klog.h> /* klogctl() */
84
#endif /* __linux__ */
87
/* All Avahi types, constants and functions
43
90
#include <avahi-core/core.h>
44
91
#include <avahi-core/lookup.h>
45
92
#include <avahi-core/log.h>
47
94
#include <avahi-common/malloc.h>
48
95
#include <avahi-common/error.h>
51
#include <sys/types.h> /* socket(), setsockopt(),
53
#include <sys/socket.h> /* socket(), setsockopt(),
55
struct in6_addr, inet_pton() */
56
#include <gnutls/gnutls.h> /* All GnuTLS stuff */
57
#include <gnutls/openpgp.h> /* GnuTLS with openpgp stuff */
59
#include <unistd.h> /* close() */
60
#include <netinet/in.h>
61
#include <stdbool.h> /* true */
62
#include <string.h> /* memset */
63
#include <arpa/inet.h> /* inet_pton() */
64
#include <iso646.h> /* not */
67
#include <errno.h> /* perror() */
74
#define CERT_ROOT "/conf/conf.d/cryptkeyreq/"
76
#define CERTFILE CERT_ROOT "openpgp-client.txt"
77
#define KEYFILE CERT_ROOT "openpgp-client-key.txt"
98
#include <gnutls/gnutls.h> /* All GnuTLS types, constants and
101
init_gnutls_session(),
103
#include <gnutls/openpgp.h>
104
/* gnutls_certificate_set_openpgp_key_file(),
105
GNUTLS_OPENPGP_FMT_BASE64 */
108
#include <gpgme.h> /* All GPGME types, constants and
111
GPGME_PROTOCOL_OpenPGP,
78
114
#define BUFFER_SIZE 256
116
#define PATHDIR "/conf/conf.d/mandos"
117
#define SECKEY "seckey.txt"
118
#define PUBKEY "pubkey.txt"
81
120
bool debug = false;
121
static const char mandos_protocol_version[] = "1";
122
const char *argp_program_version = "mandos-client " VERSION;
123
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
125
/* Used for passing in values through the Avahi callback functions */
84
gnutls_session_t session;
127
AvahiSimplePoll *simple_poll;
85
129
gnutls_certificate_credentials_t cred;
130
unsigned int dh_bits;
86
131
gnutls_dh_params_t dh_params;
90
ssize_t pgp_packet_decrypt (char *packet, size_t packet_size,
91
char **new_packet, const char *homedir){
92
gpgme_data_t dh_crypto, dh_plain;
132
const char *priority;
136
/* global context so signal handler can reach it*/
137
mandos_context mc = { .simple_poll = NULL, .server = NULL,
138
.dh_bits = 1024, .priority = "SECURE256"
139
":!CTYPE-X.509:+CTYPE-OPENPGP" };
142
* Make additional room in "buffer" for at least BUFFER_SIZE more
143
* bytes. "buffer_capacity" is how much is currently allocated,
144
* "buffer_length" is how much is already used.
146
size_t incbuffer(char **buffer, size_t buffer_length,
147
size_t buffer_capacity){
148
if(buffer_length + BUFFER_SIZE > buffer_capacity){
149
*buffer = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
153
buffer_capacity += BUFFER_SIZE;
155
return buffer_capacity;
161
static bool init_gpgme(const char *seckey,
162
const char *pubkey, const char *tempdir){
96
ssize_t new_packet_capacity = 0;
97
ssize_t new_packet_length = 0;
98
165
gpgme_engine_info_t engine_info;
101
fprintf(stderr, "Trying to decrypt OpenPGP packet\n");
169
* Helper function to insert pub and seckey to the engine keyring.
171
bool import_key(const char *filename){
173
gpgme_data_t pgp_data;
175
fd = (int)TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
181
rc = gpgme_data_new_from_fd(&pgp_data, fd);
182
if(rc != GPG_ERR_NO_ERROR){
183
fprintf(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
184
gpgme_strsource(rc), gpgme_strerror(rc));
188
rc = gpgme_op_import(mc.ctx, pgp_data);
189
if(rc != GPG_ERR_NO_ERROR){
190
fprintf(stderr, "bad gpgme_op_import: %s: %s\n",
191
gpgme_strsource(rc), gpgme_strerror(rc));
195
ret = (int)TEMP_FAILURE_RETRY(close(fd));
199
gpgme_data_release(pgp_data);
204
fprintf(stderr, "Initializing GPGME\n");
105
208
gpgme_check_version(NULL);
106
gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
209
rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
210
if(rc != GPG_ERR_NO_ERROR){
211
fprintf(stderr, "bad gpgme_engine_check_version: %s: %s\n",
212
gpgme_strsource(rc), gpgme_strerror(rc));
108
/* Set GPGME home directory */
109
rc = gpgme_get_engine_info (&engine_info);
110
if (rc != GPG_ERR_NO_ERROR){
216
/* Set GPGME home directory for the OpenPGP engine only */
217
rc = gpgme_get_engine_info(&engine_info);
218
if(rc != GPG_ERR_NO_ERROR){
111
219
fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
112
220
gpgme_strsource(rc), gpgme_strerror(rc));
115
223
while(engine_info != NULL){
116
224
if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
117
225
gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
118
engine_info->file_name, homedir);
226
engine_info->file_name, tempdir);
121
229
engine_info = engine_info->next;
123
231
if(engine_info == NULL){
124
fprintf(stderr, "Could not set home dir to %s\n", homedir);
128
/* Create new GPGME data buffer from packet buffer */
129
rc = gpgme_data_new_from_mem(&dh_crypto, packet, packet_size, 0);
130
if (rc != GPG_ERR_NO_ERROR){
232
fprintf(stderr, "Could not set GPGME home dir to %s\n", tempdir);
236
/* Create new GPGME "context" */
237
rc = gpgme_new(&(mc.ctx));
238
if(rc != GPG_ERR_NO_ERROR){
239
fprintf(stderr, "bad gpgme_new: %s: %s\n",
240
gpgme_strsource(rc), gpgme_strerror(rc));
244
if(not import_key(pubkey) or not import_key(seckey)){
252
* Decrypt OpenPGP data.
253
* Returns -1 on error
255
static ssize_t pgp_packet_decrypt(const char *cryptotext,
258
gpgme_data_t dh_crypto, dh_plain;
261
size_t plaintext_capacity = 0;
262
ssize_t plaintext_length = 0;
265
fprintf(stderr, "Trying to decrypt OpenPGP data\n");
268
/* Create new GPGME data buffer from memory cryptotext */
269
rc = gpgme_data_new_from_mem(&dh_crypto, cryptotext, crypto_size,
271
if(rc != GPG_ERR_NO_ERROR){
131
272
fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
132
273
gpgme_strsource(rc), gpgme_strerror(rc));
136
277
/* Create new empty GPGME data buffer for the plaintext */
137
278
rc = gpgme_data_new(&dh_plain);
138
if (rc != GPG_ERR_NO_ERROR){
279
if(rc != GPG_ERR_NO_ERROR){
139
280
fprintf(stderr, "bad gpgme_data_new: %s: %s\n",
140
281
gpgme_strsource(rc), gpgme_strerror(rc));
144
/* Create new GPGME "context" */
145
rc = gpgme_new(&ctx);
146
if (rc != GPG_ERR_NO_ERROR){
147
fprintf(stderr, "bad gpgme_new: %s: %s\n",
148
gpgme_strsource(rc), gpgme_strerror(rc));
152
/* Decrypt data from the FILE pointer to the plaintext data
154
rc = gpgme_op_decrypt(ctx, dh_crypto, dh_plain);
155
if (rc != GPG_ERR_NO_ERROR){
282
gpgme_data_release(dh_crypto);
286
/* Decrypt data from the cryptotext data buffer to the plaintext
288
rc = gpgme_op_decrypt(mc.ctx, dh_crypto, dh_plain);
289
if(rc != GPG_ERR_NO_ERROR){
156
290
fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
157
291
gpgme_strsource(rc), gpgme_strerror(rc));
292
plaintext_length = -1;
294
gpgme_decrypt_result_t result;
295
result = gpgme_op_decrypt_result(mc.ctx);
297
fprintf(stderr, "gpgme_op_decrypt_result failed\n");
299
fprintf(stderr, "Unsupported algorithm: %s\n",
300
result->unsupported_algorithm);
301
fprintf(stderr, "Wrong key usage: %u\n",
302
result->wrong_key_usage);
303
if(result->file_name != NULL){
304
fprintf(stderr, "File name: %s\n", result->file_name);
306
gpgme_recipient_t recipient;
307
recipient = result->recipients;
309
while(recipient != NULL){
310
fprintf(stderr, "Public key algorithm: %s\n",
311
gpgme_pubkey_algo_name(recipient->pubkey_algo));
312
fprintf(stderr, "Key ID: %s\n", recipient->keyid);
313
fprintf(stderr, "Secret key available: %s\n",
314
recipient->status == GPG_ERR_NO_SECKEY
316
recipient = recipient->next;
162
fprintf(stderr, "Decryption of OpenPGP packet succeeded\n");
166
gpgme_decrypt_result_t result;
167
result = gpgme_op_decrypt_result(ctx);
169
fprintf(stderr, "gpgme_op_decrypt_result failed\n");
171
fprintf(stderr, "Unsupported algorithm: %s\n",
172
result->unsupported_algorithm);
173
fprintf(stderr, "Wrong key usage: %d\n",
174
result->wrong_key_usage);
175
if(result->file_name != NULL){
176
fprintf(stderr, "File name: %s\n", result->file_name);
178
gpgme_recipient_t recipient;
179
recipient = result->recipients;
181
while(recipient != NULL){
182
fprintf(stderr, "Public key algorithm: %s\n",
183
gpgme_pubkey_algo_name(recipient->pubkey_algo));
184
fprintf(stderr, "Key ID: %s\n", recipient->keyid);
185
fprintf(stderr, "Secret key available: %s\n",
186
recipient->status == GPG_ERR_NO_SECKEY
188
recipient = recipient->next;
194
/* Delete the GPGME FILE pointer cryptotext data buffer */
195
gpgme_data_release(dh_crypto);
325
fprintf(stderr, "Decryption of OpenPGP data succeeded\n");
197
328
/* Seek back to the beginning of the GPGME plaintext data buffer */
198
gpgme_data_seek(dh_plain, 0, SEEK_SET);
329
if(gpgme_data_seek(dh_plain, (off_t)0, SEEK_SET) == -1){
330
perror("gpgme_data_seek");
331
plaintext_length = -1;
202
if (new_packet_length + BUFFER_SIZE > new_packet_capacity){
203
*new_packet = realloc(*new_packet,
204
(unsigned int)new_packet_capacity
206
if (*new_packet == NULL){
210
new_packet_capacity += BUFFER_SIZE;
337
plaintext_capacity = incbuffer(plaintext,
338
(size_t)plaintext_length,
340
if(plaintext_capacity == 0){
342
plaintext_length = -1;
213
ret = gpgme_data_read(dh_plain, *new_packet + new_packet_length,
346
ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
215
348
/* Print the data, if any */
220
354
perror("gpgme_data_read");
223
new_packet_length += ret;
226
/* FIXME: check characters before printing to screen so to not print
227
terminal control characters */
229
/* fprintf(stderr, "decrypted password is: "); */
230
/* fwrite(*new_packet, 1, new_packet_length, stderr); */
231
/* fprintf(stderr, "\n"); */
355
plaintext_length = -1;
358
plaintext_length += ret;
362
fprintf(stderr, "Decrypted password is: ");
363
for(ssize_t i = 0; i < plaintext_length; i++){
364
fprintf(stderr, "%02hhX ", (*plaintext)[i]);
366
fprintf(stderr, "\n");
371
/* Delete the GPGME cryptotext data buffer */
372
gpgme_data_release(dh_crypto);
234
374
/* Delete the GPGME plaintext data buffer */
235
375
gpgme_data_release(dh_plain);
236
return new_packet_length;
376
return plaintext_length;
239
static const char * safer_gnutls_strerror (int value) {
240
const char *ret = gnutls_strerror (value);
379
static const char * safer_gnutls_strerror(int value){
380
const char *ret = gnutls_strerror(value); /* Spurious warning from
381
-Wunreachable-code */
242
383
ret = "(unknown)";
246
void debuggnutls(__attribute__((unused)) int level,
248
fprintf(stderr, "%s", string);
387
/* GnuTLS log function callback */
388
static void debuggnutls(__attribute__((unused)) int level,
390
fprintf(stderr, "GnuTLS: %s", string);
251
int initgnutls(encrypted_session *es){
393
static int init_gnutls_global(const char *pubkeyfilename,
394
const char *seckeyfilename){
256
398
fprintf(stderr, "Initializing GnuTLS\n");
259
if ((ret = gnutls_global_init ())
260
!= GNUTLS_E_SUCCESS) {
261
fprintf (stderr, "global_init: %s\n", safer_gnutls_strerror(ret));
401
ret = gnutls_global_init();
402
if(ret != GNUTLS_E_SUCCESS){
403
fprintf(stderr, "GnuTLS global_init: %s\n",
404
safer_gnutls_strerror(ret));
409
/* "Use a log level over 10 to enable all debugging options."
266
412
gnutls_global_set_log_level(11);
267
413
gnutls_global_set_log_function(debuggnutls);
270
/* openpgp credentials */
271
if ((ret = gnutls_certificate_allocate_credentials (&es->cred))
272
!= GNUTLS_E_SUCCESS) {
273
fprintf (stderr, "memory error: %s\n",
274
safer_gnutls_strerror(ret));
416
/* OpenPGP credentials */
417
gnutls_certificate_allocate_credentials(&mc.cred);
418
if(ret != GNUTLS_E_SUCCESS){
419
fprintf(stderr, "GnuTLS memory error: %s\n", /* Spurious warning
423
safer_gnutls_strerror(ret));
424
gnutls_global_deinit();
279
fprintf(stderr, "Attempting to use OpenPGP certificate %s"
280
" and keyfile %s as GnuTLS credentials\n", CERTFILE,
429
fprintf(stderr, "Attempting to use OpenPGP public key %s and"
430
" secret key %s as GnuTLS credentials\n", pubkeyfilename,
284
434
ret = gnutls_certificate_set_openpgp_key_file
285
(es->cred, CERTFILE, KEYFILE, GNUTLS_OPENPGP_FMT_BASE64);
286
if (ret != GNUTLS_E_SUCCESS) {
288
(stderr, "Error[%d] while reading the OpenPGP key pair ('%s',"
290
ret, CERTFILE, KEYFILE);
291
fprintf(stdout, "The Error is: %s\n",
292
safer_gnutls_strerror(ret));
296
//GnuTLS server initialization
297
if ((ret = gnutls_dh_params_init (&es->dh_params))
298
!= GNUTLS_E_SUCCESS) {
299
fprintf (stderr, "Error in dh parameter initialization: %s\n",
300
safer_gnutls_strerror(ret));
304
if ((ret = gnutls_dh_params_generate2 (es->dh_params, DH_BITS))
305
!= GNUTLS_E_SUCCESS) {
306
fprintf (stderr, "Error in prime generation: %s\n",
307
safer_gnutls_strerror(ret));
311
gnutls_certificate_set_dh_params (es->cred, es->dh_params);
313
// GnuTLS session creation
314
if ((ret = gnutls_init (&es->session, GNUTLS_SERVER))
315
!= GNUTLS_E_SUCCESS){
435
(mc.cred, pubkeyfilename, seckeyfilename,
436
GNUTLS_OPENPGP_FMT_BASE64);
437
if(ret != GNUTLS_E_SUCCESS){
439
"Error[%d] while reading the OpenPGP key pair ('%s',"
440
" '%s')\n", ret, pubkeyfilename, seckeyfilename);
441
fprintf(stderr, "The GnuTLS error is: %s\n",
442
safer_gnutls_strerror(ret));
446
/* GnuTLS server initialization */
447
ret = gnutls_dh_params_init(&mc.dh_params);
448
if(ret != GNUTLS_E_SUCCESS){
449
fprintf(stderr, "Error in GnuTLS DH parameter initialization:"
450
" %s\n", safer_gnutls_strerror(ret));
453
ret = gnutls_dh_params_generate2(mc.dh_params, mc.dh_bits);
454
if(ret != GNUTLS_E_SUCCESS){
455
fprintf(stderr, "Error in GnuTLS prime generation: %s\n",
456
safer_gnutls_strerror(ret));
460
gnutls_certificate_set_dh_params(mc.cred, mc.dh_params);
466
gnutls_certificate_free_credentials(mc.cred);
467
gnutls_global_deinit();
468
gnutls_dh_params_deinit(mc.dh_params);
472
static int init_gnutls_session(gnutls_session_t *session){
474
/* GnuTLS session creation */
475
ret = gnutls_init(session, GNUTLS_SERVER);
476
if(ret != GNUTLS_E_SUCCESS){
316
477
fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
317
478
safer_gnutls_strerror(ret));
320
if ((ret = gnutls_priority_set_direct (es->session, "NORMAL", &err))
321
!= GNUTLS_E_SUCCESS) {
322
fprintf(stderr, "Syntax error at: %s\n", err);
323
fprintf(stderr, "GnuTLS error: %s\n",
324
safer_gnutls_strerror(ret));
483
ret = gnutls_priority_set_direct(*session, mc.priority, &err);
484
if(ret != GNUTLS_E_SUCCESS){
485
fprintf(stderr, "Syntax error at: %s\n", err);
486
fprintf(stderr, "GnuTLS error: %s\n",
487
safer_gnutls_strerror(ret));
488
gnutls_deinit(*session);
328
if ((ret = gnutls_credentials_set
329
(es->session, GNUTLS_CRD_CERTIFICATE, es->cred))
330
!= GNUTLS_E_SUCCESS) {
331
fprintf(stderr, "Error setting a credentials set: %s\n",
493
ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
495
if(ret != GNUTLS_E_SUCCESS){
496
fprintf(stderr, "Error setting GnuTLS credentials: %s\n",
332
497
safer_gnutls_strerror(ret));
498
gnutls_deinit(*session);
336
502
/* ignore client certificate if any. */
337
gnutls_certificate_server_set_request (es->session,
503
gnutls_certificate_server_set_request(*session,
340
gnutls_dh_set_prime_bits (es->session, DH_BITS);
506
gnutls_dh_set_prime_bits(*session, mc.dh_bits);
345
void empty_log(__attribute__((unused)) AvahiLogLevel level,
346
__attribute__((unused)) const char *txt){}
511
/* Avahi log function callback */
512
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
513
__attribute__((unused)) const char *txt){}
348
int start_mandos_communication(char *ip, uint16_t port,
349
unsigned int if_index){
515
/* Called when a Mandos server is found */
516
static int start_mandos_communication(const char *ip, uint16_t port,
517
AvahiIfIndex if_index,
351
struct sockaddr_in6 to;
352
encrypted_session es;
522
struct sockaddr_in in;
523
struct sockaddr_in6 in6;
353
525
char *buffer = NULL;
354
526
char *decrypted_buffer;
355
527
size_t buffer_length = 0;
356
528
size_t buffer_capacity = 0;
357
529
ssize_t decrypted_buffer_size;
359
char interface[IF_NAMESIZE];
532
gnutls_session_t session;
533
int pf; /* Protocol family */
543
fprintf(stderr, "Bad address family: %d\n", af);
547
ret = init_gnutls_session(&session);
362
fprintf(stderr, "Setting up a tcp connection to %s\n", ip);
553
fprintf(stderr, "Setting up a TCP connection to %s, port %" PRIu16
365
tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
557
tcp_sd = socket(pf, SOCK_STREAM, 0);
367
559
perror("socket");
371
if(if_indextoname(if_index, interface) == NULL){
373
perror("if_indextoname");
379
fprintf(stderr, "Binding to interface %s\n", interface);
382
ret = setsockopt(tcp_sd, SOL_SOCKET, SO_BINDTODEVICE, interface, 5);
384
perror("setsockopt bindtodevice");
388
memset(&to,0,sizeof(to));
389
to.sin6_family = AF_INET6;
390
ret = inet_pton(AF_INET6, ip, &to.sin6_addr);
563
memset(&to, 0, sizeof(to));
565
to.in6.sin6_family = (uint16_t)af;
566
ret = inet_pton(af, ip, &to.in6.sin6_addr);
568
to.in.sin_family = (sa_family_t)af;
569
ret = inet_pton(af, ip, &to.in.sin_addr);
392
572
perror("inet_pton");
396
576
fprintf(stderr, "Bad address: %s\n", ip);
399
/* Spurious warnings for the next line, see for instance
400
<http://bugs.debian.org/488884> */
401
to.sin6_port = htons(port);
403
to.sin6_scope_id = (uint32_t)if_index;
580
to.in6.sin6_port = htons(port); /* Spurious warnings from
582
-Wunreachable-code */
584
if(IN6_IS_ADDR_LINKLOCAL /* Spurious warnings from */
585
(&to.in6.sin6_addr)){ /* -Wstrict-aliasing=2 or lower and
587
if(if_index == AVAHI_IF_UNSPEC){
588
fprintf(stderr, "An IPv6 link-local address is incomplete"
589
" without a network interface\n");
592
/* Set the network interface number as scope */
593
to.in6.sin6_scope_id = (uint32_t)if_index;
596
to.in.sin_port = htons(port); /* Spurious warnings from
598
-Wunreachable-code */
406
fprintf(stderr, "Connection to: %s\n", ip);
602
if(af == AF_INET6 and if_index != AVAHI_IF_UNSPEC){
603
char interface[IF_NAMESIZE];
604
if(if_indextoname((unsigned int)if_index, interface) == NULL){
605
perror("if_indextoname");
607
fprintf(stderr, "Connection to: %s%%%s, port %" PRIu16 "\n",
608
ip, interface, port);
611
fprintf(stderr, "Connection to: %s, port %" PRIu16 "\n", ip,
614
char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ?
615
INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = "";
618
pcret = inet_ntop(af, &(to.in6.sin6_addr), addrstr,
621
pcret = inet_ntop(af, &(to.in.sin_addr), addrstr,
627
if(strcmp(addrstr, ip) != 0){
628
fprintf(stderr, "Canonical address form: %s\n", addrstr);
409
ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
634
ret = connect(tcp_sd, &to.in6, sizeof(to));
636
ret = connect(tcp_sd, &to.in, sizeof(to)); /* IPv4 */
411
639
perror("connect");
415
ret = initgnutls (&es);
643
const char *out = mandos_protocol_version;
646
size_t out_size = strlen(out);
647
ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
648
out_size - written));
654
written += (size_t)ret;
655
if(written < out_size){
658
if(out == mandos_protocol_version){
421
gnutls_transport_set_ptr (es.session,
422
(gnutls_transport_ptr_t) tcp_sd);
425
668
fprintf(stderr, "Establishing TLS session with %s\n", ip);
428
ret = gnutls_handshake (es.session);
430
if (ret != GNUTLS_E_SUCCESS){
431
fprintf(stderr, "\n*** Handshake failed ***\n");
671
gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t) tcp_sd);
674
ret = gnutls_handshake(session);
675
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
677
if(ret != GNUTLS_E_SUCCESS){
679
fprintf(stderr, "*** GnuTLS Handshake failed ***\n");
437
//Retrieve OpenPGP packet that contains the wanted password
686
/* Read OpenPGP packet that contains the wanted password */
440
fprintf(stderr, "Retrieving pgp encrypted password from %s\n",
689
fprintf(stderr, "Retrieving OpenPGP encrypted password from %s\n",
445
if (buffer_length + BUFFER_SIZE > buffer_capacity){
446
buffer = realloc(buffer, buffer_capacity + BUFFER_SIZE);
451
buffer_capacity += BUFFER_SIZE;
694
buffer_capacity = incbuffer(&buffer, buffer_length,
696
if(buffer_capacity == 0){
454
ret = gnutls_record_recv
455
(es.session, buffer+buffer_length, BUFFER_SIZE);
702
sret = gnutls_record_recv(session, buffer+buffer_length,
461
709
case GNUTLS_E_INTERRUPTED:
462
710
case GNUTLS_E_AGAIN:
464
712
case GNUTLS_E_REHANDSHAKE:
465
ret = gnutls_handshake (es.session);
467
fprintf(stderr, "\n*** Handshake failed ***\n");
714
ret = gnutls_handshake(session);
715
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
717
fprintf(stderr, "*** GnuTLS Re-handshake failed ***\n");
474
724
fprintf(stderr, "Unknown error while reading data from"
475
" encrypted session with mandos server\n");
725
" encrypted session with Mandos server\n");
477
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
727
gnutls_bye(session, GNUTLS_SHUT_RDWR);
481
buffer_length += (size_t) ret;
731
buffer_length += (size_t) sret;
485
if (buffer_length > 0){
736
fprintf(stderr, "Closing TLS session\n");
739
gnutls_bye(session, GNUTLS_SHUT_RDWR);
741
if(buffer_length > 0){
486
742
decrypted_buffer_size = pgp_packet_decrypt(buffer,
490
if (decrypted_buffer_size >= 0){
491
while(decrypted_buffer_size > 0){
492
ret = fwrite (decrypted_buffer, 1, (size_t)decrypted_buffer_size,
745
if(decrypted_buffer_size >= 0){
747
while(written < (size_t) decrypted_buffer_size){
748
ret = (int)fwrite(decrypted_buffer + written, 1,
749
(size_t)decrypted_buffer_size - written,
494
751
if(ret == 0 and ferror(stdout)){
496
753
fprintf(stderr, "Error writing encrypted data: %s\n",
502
decrypted_buffer += ret;
503
decrypted_buffer_size -= ret;
759
written += (size_t)ret;
505
761
free(decrypted_buffer);
514
fprintf(stderr, "Closing TLS session\n");
769
/* Shutdown procedure */
518
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
521
gnutls_deinit (es.session);
522
gnutls_certificate_free_credentials (es.cred);
523
gnutls_global_deinit ();
773
ret = (int)TEMP_FAILURE_RETRY(close(tcp_sd));
777
gnutls_deinit(session);
527
static AvahiSimplePoll *simple_poll = NULL;
528
static AvahiServer *server = NULL;
530
static void resolve_callback(
531
AvahiSServiceResolver *r,
532
AVAHI_GCC_UNUSED AvahiIfIndex interface,
533
AVAHI_GCC_UNUSED AvahiProtocol protocol,
534
AvahiResolverEvent event,
538
const char *host_name,
539
const AvahiAddress *address,
541
AVAHI_GCC_UNUSED AvahiStringList *txt,
542
AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
543
AVAHI_GCC_UNUSED void* userdata) {
781
static void resolve_callback(AvahiSServiceResolver *r,
782
AvahiIfIndex interface,
784
AvahiResolverEvent event,
788
const char *host_name,
789
const AvahiAddress *address,
791
AVAHI_GCC_UNUSED AvahiStringList *txt,
792
AVAHI_GCC_UNUSED AvahiLookupResultFlags
794
AVAHI_GCC_UNUSED void* userdata){
547
797
/* Called whenever a service has been resolved successfully or
552
802
case AVAHI_RESOLVER_FAILURE:
553
fprintf(stderr, "(Resolver) Failed to resolve service '%s' of"
554
" type '%s' in domain '%s': %s\n", name, type, domain,
555
avahi_strerror(avahi_server_errno(server)));
803
fprintf(stderr, "(Avahi Resolver) Failed to resolve service '%s'"
804
" of type '%s' in domain '%s': %s\n", name, type, domain,
805
avahi_strerror(avahi_server_errno(mc.server)));
558
808
case AVAHI_RESOLVER_FOUND:
560
810
char ip[AVAHI_ADDRESS_STR_MAX];
561
811
avahi_address_snprint(ip, sizeof(ip), address);
563
fprintf(stderr, "Mandos server found on %s (%s) on port %d\n",
564
host_name, ip, port);
813
fprintf(stderr, "Mandos server \"%s\" found on %s (%s, %"
814
PRIdMAX ") on port %" PRIu16 "\n", name, host_name,
815
ip, (intmax_t)interface, port);
566
int ret = start_mandos_communication(ip, port,
817
int ret = start_mandos_communication(ip, port, interface,
818
avahi_proto_to_af(proto));
820
avahi_simple_poll_quit(mc.simple_poll);
576
824
avahi_s_service_resolver_free(r);
579
static void browse_callback(
580
AvahiSServiceBrowser *b,
581
AvahiIfIndex interface,
582
AvahiProtocol protocol,
583
AvahiBrowserEvent event,
587
AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
590
AvahiServer *s = userdata;
593
/* Called whenever a new services becomes available on the LAN or
594
is removed from the LAN */
598
case AVAHI_BROWSER_FAILURE:
600
fprintf(stderr, "(Browser) %s\n",
601
avahi_strerror(avahi_server_errno(server)));
602
avahi_simple_poll_quit(simple_poll);
605
case AVAHI_BROWSER_NEW:
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. */
611
if (!(avahi_s_service_resolver_new(s, interface, protocol, name,
613
AVAHI_PROTO_INET6, 0,
614
resolve_callback, s)))
615
fprintf(stderr, "Failed to resolve service '%s': %s\n", name,
616
avahi_strerror(avahi_server_errno(s)));
619
case AVAHI_BROWSER_REMOVE:
622
case AVAHI_BROWSER_ALL_FOR_NOW:
623
case AVAHI_BROWSER_CACHE_EXHAUSTED:
628
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
827
static void browse_callback(AvahiSServiceBrowser *b,
828
AvahiIfIndex interface,
829
AvahiProtocol protocol,
830
AvahiBrowserEvent event,
834
AVAHI_GCC_UNUSED AvahiLookupResultFlags
836
AVAHI_GCC_UNUSED void* userdata){
839
/* Called whenever a new services becomes available on the LAN or
840
is removed from the LAN */
844
case AVAHI_BROWSER_FAILURE:
846
fprintf(stderr, "(Avahi browser) %s\n",
847
avahi_strerror(avahi_server_errno(mc.server)));
848
avahi_simple_poll_quit(mc.simple_poll);
851
case AVAHI_BROWSER_NEW:
852
/* We ignore the returned Avahi resolver object. In the callback
853
function we free it. If the Avahi server is terminated before
854
the callback function is called the Avahi server will free the
857
if(avahi_s_service_resolver_new(mc.server, interface, protocol,
858
name, type, domain, protocol, 0,
859
resolve_callback, NULL) == NULL)
860
fprintf(stderr, "Avahi: Failed to resolve service '%s': %s\n",
861
name, avahi_strerror(avahi_server_errno(mc.server)));
864
case AVAHI_BROWSER_REMOVE:
867
case AVAHI_BROWSER_ALL_FOR_NOW:
868
case AVAHI_BROWSER_CACHE_EXHAUSTED:
870
fprintf(stderr, "No Mandos server found, still searching...\n");
876
sig_atomic_t quit_now = 0;
878
/* stop main loop after sigterm has been called */
879
static void handle_sigterm(__attribute__((unused)) int sig){
884
int old_errno = errno;
885
if(mc.simple_poll != NULL){
886
avahi_simple_poll_quit(mc.simple_poll);
891
int main(int argc, char *argv[]){
892
AvahiSServiceBrowser *sb = NULL;
897
int exitcode = EXIT_SUCCESS;
898
const char *interface = "eth0";
899
struct ifreq network;
903
char *connect_to = NULL;
904
char tempdir[] = "/tmp/mandosXXXXXX";
905
bool tempdir_created = false;
906
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
907
const char *seckey = PATHDIR "/" SECKEY;
908
const char *pubkey = PATHDIR "/" PUBKEY;
910
bool gnutls_initialized = false;
911
bool gpgme_initialized = false;
914
struct sigaction old_sigterm_action;
915
struct sigaction sigterm_action = { .sa_handler = handle_sigterm };
918
struct argp_option options[] = {
919
{ .name = "debug", .key = 128,
920
.doc = "Debug mode", .group = 3 },
921
{ .name = "connect", .key = 'c',
922
.arg = "ADDRESS:PORT",
923
.doc = "Connect directly to a specific Mandos server",
925
{ .name = "interface", .key = 'i',
927
.doc = "Network interface that will be used to search for"
930
{ .name = "seckey", .key = 's',
932
.doc = "OpenPGP secret key file base name",
934
{ .name = "pubkey", .key = 'p',
936
.doc = "OpenPGP public key file base name",
938
{ .name = "dh-bits", .key = 129,
940
.doc = "Bit length of the prime number used in the"
941
" Diffie-Hellman key exchange",
943
{ .name = "priority", .key = 130,
945
.doc = "GnuTLS priority string for the TLS handshake",
947
{ .name = "delay", .key = 131,
949
.doc = "Maximum delay to wait for interface startup",
954
error_t parse_opt(int key, char *arg,
955
struct argp_state *state){
957
case 128: /* --debug */
960
case 'c': /* --connect */
963
case 'i': /* --interface */
966
case 's': /* --seckey */
969
case 'p': /* --pubkey */
972
case 129: /* --dh-bits */
974
tmpmax = strtoimax(arg, &tmp, 10);
975
if(errno != 0 or tmp == arg or *tmp != '\0'
976
or tmpmax != (typeof(mc.dh_bits))tmpmax){
977
fprintf(stderr, "Bad number of DH bits\n");
980
mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
982
case 130: /* --priority */
985
case 131: /* --delay */
987
delay = strtof(arg, &tmp);
988
if(errno != 0 or tmp == arg or *tmp != '\0'){
989
fprintf(stderr, "Bad delay\n");
998
return ARGP_ERR_UNKNOWN;
1003
struct argp argp = { .options = options, .parser = parse_opt,
1005
.doc = "Mandos client -- Get and decrypt"
1006
" passwords from a Mandos server" };
1007
ret = argp_parse(&argp, argc, argv, 0, 0, NULL);
1008
if(ret == ARGP_ERR_UNKNOWN){
1009
fprintf(stderr, "Unknown error while parsing arguments\n");
1010
exitcode = EXIT_FAILURE;
1016
avahi_set_log_function(empty_log);
1019
/* Initialize Avahi early so avahi_simple_poll_quit() can be called
1020
from the signal handler */
1021
/* Initialize the pseudo-RNG for Avahi */
1022
srand((unsigned int) time(NULL));
1023
mc.simple_poll = avahi_simple_poll_new();
1024
if(mc.simple_poll == NULL){
1025
fprintf(stderr, "Avahi: Failed to create simple poll object.\n");
1026
exitcode = EXIT_FAILURE;
1030
sigemptyset(&sigterm_action.sa_mask);
1031
ret = sigaddset(&sigterm_action.sa_mask, SIGINT);
1033
perror("sigaddset");
1034
exitcode = EXIT_FAILURE;
1037
ret = sigaddset(&sigterm_action.sa_mask, SIGHUP);
1039
perror("sigaddset");
1040
exitcode = EXIT_FAILURE;
1043
ret = sigaddset(&sigterm_action.sa_mask, SIGTERM);
1045
perror("sigaddset");
1046
exitcode = EXIT_FAILURE;
1049
ret = sigaction(SIGTERM, &sigterm_action, &old_sigterm_action);
1051
perror("sigaction");
1052
exitcode = EXIT_FAILURE;
1056
/* If the interface is down, bring it up */
1057
if(interface[0] != '\0'){
1059
/* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
1060
messages to mess up the prompt */
1061
ret = klogctl(8, NULL, 5);
1062
bool restore_loglevel = true;
1064
restore_loglevel = false;
1067
#endif /* __linux__ */
1069
sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1072
exitcode = EXIT_FAILURE;
1074
if(restore_loglevel){
1075
ret = klogctl(7, NULL, 0);
1080
#endif /* __linux__ */
1083
strcpy(network.ifr_name, interface);
1084
ret = ioctl(sd, SIOCGIFFLAGS, &network);
1086
perror("ioctl SIOCGIFFLAGS");
1088
if(restore_loglevel){
1089
ret = klogctl(7, NULL, 0);
1094
#endif /* __linux__ */
1095
exitcode = EXIT_FAILURE;
1098
if((network.ifr_flags & IFF_UP) == 0){
1099
network.ifr_flags |= IFF_UP;
1100
ret = ioctl(sd, SIOCSIFFLAGS, &network);
1102
perror("ioctl SIOCSIFFLAGS");
1103
exitcode = EXIT_FAILURE;
1105
if(restore_loglevel){
1106
ret = klogctl(7, NULL, 0);
1111
#endif /* __linux__ */
1115
/* sleep checking until interface is running */
1116
for(int i=0; i < delay * 4; i++){
1117
ret = ioctl(sd, SIOCGIFFLAGS, &network);
1119
perror("ioctl SIOCGIFFLAGS");
1120
} else if(network.ifr_flags & IFF_RUNNING){
1123
struct timespec sleeptime = { .tv_nsec = 250000000 };
1124
ret = nanosleep(&sleeptime, NULL);
1125
if(ret == -1 and errno != EINTR){
1126
perror("nanosleep");
1129
ret = (int)TEMP_FAILURE_RETRY(close(sd));
1134
if(restore_loglevel){
1135
/* Restores kernel loglevel to default */
1136
ret = klogctl(7, NULL, 0);
1141
#endif /* __linux__ */
1158
ret = init_gnutls_global(pubkey, seckey);
1160
fprintf(stderr, "init_gnutls_global failed\n");
1161
exitcode = EXIT_FAILURE;
1164
gnutls_initialized = true;
1167
if(mkdtemp(tempdir) == NULL){
1171
tempdir_created = true;
1173
if(not init_gpgme(pubkey, seckey, tempdir)){
1174
fprintf(stderr, "init_gpgme failed\n");
1175
exitcode = EXIT_FAILURE;
1178
gpgme_initialized = true;
1181
if(interface[0] != '\0'){
1182
if_index = (AvahiIfIndex) if_nametoindex(interface);
1184
fprintf(stderr, "No such interface: \"%s\"\n", interface);
1185
exitcode = EXIT_FAILURE;
1190
if(connect_to != NULL){
1191
/* Connect directly, do not use Zeroconf */
1192
/* (Mainly meant for debugging) */
1193
char *address = strrchr(connect_to, ':');
1194
if(address == NULL){
1195
fprintf(stderr, "No colon in address\n");
1196
exitcode = EXIT_FAILURE;
1201
tmpmax = strtoimax(address+1, &tmp, 10);
1202
if(errno != 0 or tmp == address+1 or *tmp != '\0'
1203
or tmpmax != (uint16_t)tmpmax){
1204
fprintf(stderr, "Bad port number\n");
1205
exitcode = EXIT_FAILURE;
1208
port = (uint16_t)tmpmax;
1210
address = connect_to;
1211
/* Colon in address indicates IPv6 */
1213
if(strchr(address, ':') != NULL){
1218
ret = start_mandos_communication(address, port, if_index, af);
1220
exitcode = EXIT_FAILURE;
1222
exitcode = EXIT_SUCCESS;
629
1228
AvahiServerConfig config;
630
AvahiSServiceBrowser *sb = NULL;
633
int returncode = EXIT_SUCCESS;
634
const char *interface = "eth0";
637
static struct option long_options[] = {
638
{"debug", no_argument, (int *)&debug, 1},
639
{"interface", required_argument, 0, 'i'},
642
int option_index = 0;
643
ret = getopt_long (argc, argv, "i:", long_options,
662
avahi_set_log_function(empty_log);
665
/* Initialize the psuedo-RNG */
666
srand((unsigned int) time(NULL));
668
/* Allocate main loop object */
669
if (!(simple_poll = avahi_simple_poll_new())) {
670
fprintf(stderr, "Failed to create simple poll object.\n");
675
/* Do not publish any local records */
1229
/* Do not publish any local Zeroconf records */
676
1230
avahi_server_config_init(&config);
677
1231
config.publish_hinfo = 0;
678
1232
config.publish_addresses = 0;
679
1233
config.publish_workstation = 0;
680
1234
config.publish_domain = 0;
682
1236
/* Allocate a new server */
683
server = avahi_server_new(avahi_simple_poll_get(simple_poll),
684
&config, NULL, NULL, &error);
686
/* Free the configuration data */
1237
mc.server = avahi_server_new(avahi_simple_poll_get
1238
(mc.simple_poll), &config, NULL,
1241
/* Free the Avahi configuration data */
687
1242
avahi_server_config_free(&config);
689
/* Check if creating the server object succeeded */
691
fprintf(stderr, "Failed to create server: %s\n",
692
avahi_strerror(error));
693
returncode = EXIT_FAILURE;
697
/* Create the service browser */
698
sb = avahi_s_service_browser_new(server,
700
if_nametoindex(interface),
702
"_mandos._tcp", NULL, 0,
703
browse_callback, server);
705
fprintf(stderr, "Failed to create service browser: %s\n",
706
avahi_strerror(avahi_server_errno(server)));
707
returncode = EXIT_FAILURE;
711
/* Run the main loop */
714
fprintf(stderr, "Starting avahi loop search\n");
717
avahi_simple_poll_loop(simple_poll);
722
fprintf(stderr, "%s exiting\n", argv[0]);
727
avahi_s_service_browser_free(sb);
730
avahi_server_free(server);
733
avahi_simple_poll_free(simple_poll);
1245
/* Check if creating the Avahi server object succeeded */
1246
if(mc.server == NULL){
1247
fprintf(stderr, "Failed to create Avahi server: %s\n",
1248
avahi_strerror(error));
1249
exitcode = EXIT_FAILURE;
1253
/* Create the Avahi service browser */
1254
sb = avahi_s_service_browser_new(mc.server, if_index,
1255
AVAHI_PROTO_UNSPEC, "_mandos._tcp",
1256
NULL, 0, browse_callback, NULL);
1258
fprintf(stderr, "Failed to create service browser: %s\n",
1259
avahi_strerror(avahi_server_errno(mc.server)));
1260
exitcode = EXIT_FAILURE;
1264
/* Run the main loop */
1267
fprintf(stderr, "Starting Avahi loop search\n");
1270
avahi_simple_poll_loop(mc.simple_poll);
1275
fprintf(stderr, "%s exiting\n", argv[0]);
1278
/* Cleanup things */
1280
avahi_s_service_browser_free(sb);
1282
if(mc.server != NULL)
1283
avahi_server_free(mc.server);
1285
if(mc.simple_poll != NULL)
1286
avahi_simple_poll_free(mc.simple_poll);
1288
if(gnutls_initialized){
1289
gnutls_certificate_free_credentials(mc.cred);
1290
gnutls_global_deinit();
1291
gnutls_dh_params_deinit(mc.dh_params);
1294
if(gpgme_initialized){
1295
gpgme_release(mc.ctx);
1298
/* Removes the temp directory used by GPGME */
1299
if(tempdir_created){
1301
struct dirent *direntry;
1302
d = opendir(tempdir);
1304
if(errno != ENOENT){
1309
direntry = readdir(d);
1310
if(direntry == NULL){
1313
/* Skip "." and ".." */
1314
if(direntry->d_name[0] == '.'
1315
and (direntry->d_name[1] == '\0'
1316
or (direntry->d_name[1] == '.'
1317
and direntry->d_name[2] == '\0'))){
1320
char *fullname = NULL;
1321
ret = asprintf(&fullname, "%s/%s", tempdir,
1327
ret = remove(fullname);
1329
fprintf(stderr, "remove(\"%s\"): %s\n", fullname,
1336
ret = rmdir(tempdir);
1337
if(ret == -1 and errno != ENOENT){