26
25
* along with this program. If not, see
27
26
* <http://www.gnu.org/licenses/>.
29
* Contact the authors at <mandos@fukt.bsnet.se>.
28
* Contact the authors at <https://www.fukt.bsnet.se/~belorn/> and
29
* <https://www.fukt.bsnet.se/~teddy/>.
32
/* Needed by GPGME, specifically gpgme_data_seek() */
32
#define _FORTIFY_SOURCE 2
33
34
#define _LARGEFILE_SOURCE
34
35
#define _FILE_OFFSET_BITS 64
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
41
#include <net/if.h> /* if_nametoindex */
90
43
#include <avahi-core/core.h>
91
44
#include <avahi-core/lookup.h>
92
45
#include <avahi-core/log.h>
94
47
#include <avahi-common/malloc.h>
95
48
#include <avahi-common/error.h>
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,
51
#include <sys/types.h> /* socket(), inet_pton() */
52
#include <sys/socket.h> /* socket(), struct sockaddr_in6,
53
struct in6_addr, inet_pton() */
54
#include <gnutls/gnutls.h> /* All GnuTLS stuff */
55
#include <gnutls/openpgp.h> /* GnuTLS with openpgp stuff */
57
#include <unistd.h> /* close() */
58
#include <netinet/in.h>
59
#include <stdbool.h> /* true */
60
#include <string.h> /* memset */
61
#include <arpa/inet.h> /* inet_pton() */
62
#include <iso646.h> /* not */
65
#include <errno.h> /* perror() */
72
#define CERT_ROOT "/conf/conf.d/cryptkeyreq/"
74
#define CERTFILE CERT_ROOT "openpgp-client.txt"
75
#define KEYFILE CERT_ROOT "openpgp-client-key.txt"
114
76
#define BUFFER_SIZE 256
116
#define PATHDIR "/conf/conf.d/mandos"
117
#define SECKEY "seckey.txt"
118
#define PUBKEY "pubkey.txt"
120
79
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 */
127
AvahiSimplePoll *simple_poll;
82
gnutls_session_t session;
129
83
gnutls_certificate_credentials_t cred;
130
unsigned int dh_bits;
131
84
gnutls_dh_params_t dh_params;
132
const char *priority;
88
ssize_t pgp_packet_decrypt (char *packet, size_t packet_size,
89
char **new_packet, const char *homedir){
90
gpgme_data_t dh_crypto, dh_plain;
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){
94
ssize_t new_packet_capacity = 0;
95
ssize_t new_packet_length = 0;
165
96
gpgme_engine_info_t engine_info;
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");
99
fprintf(stderr, "Trying to decrypt OpenPGP packet\n");
208
103
gpgme_check_version(NULL);
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));
104
gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
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){
106
/* Set GPGME home directory */
107
rc = gpgme_get_engine_info (&engine_info);
108
if (rc != GPG_ERR_NO_ERROR){
219
109
fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
220
110
gpgme_strsource(rc), gpgme_strerror(rc));
223
113
while(engine_info != NULL){
224
114
if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
225
115
gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
226
engine_info->file_name, tempdir);
116
engine_info->file_name, homedir);
229
119
engine_info = engine_info->next;
231
121
if(engine_info == NULL){
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){
122
fprintf(stderr, "Could not set home dir to %s\n", homedir);
126
/* Create new GPGME data buffer from packet buffer */
127
rc = gpgme_data_new_from_mem(&dh_crypto, packet, packet_size, 0);
128
if (rc != GPG_ERR_NO_ERROR){
272
129
fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
273
130
gpgme_strsource(rc), gpgme_strerror(rc));
277
134
/* Create new empty GPGME data buffer for the plaintext */
278
135
rc = gpgme_data_new(&dh_plain);
279
if(rc != GPG_ERR_NO_ERROR){
136
if (rc != GPG_ERR_NO_ERROR){
280
137
fprintf(stderr, "bad gpgme_data_new: %s: %s\n",
281
138
gpgme_strsource(rc), gpgme_strerror(rc));
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){
142
/* Create new GPGME "context" */
143
rc = gpgme_new(&ctx);
144
if (rc != GPG_ERR_NO_ERROR){
145
fprintf(stderr, "bad gpgme_new: %s: %s\n",
146
gpgme_strsource(rc), gpgme_strerror(rc));
150
/* Decrypt data from the FILE pointer to the plaintext data
152
rc = gpgme_op_decrypt(ctx, dh_crypto, dh_plain);
153
if (rc != GPG_ERR_NO_ERROR){
290
154
fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
291
155
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;
160
fprintf(stderr, "Decryption of OpenPGP packet succeeded\n");
164
gpgme_decrypt_result_t result;
165
result = gpgme_op_decrypt_result(ctx);
167
fprintf(stderr, "gpgme_op_decrypt_result failed\n");
169
fprintf(stderr, "Unsupported algorithm: %s\n",
170
result->unsupported_algorithm);
171
fprintf(stderr, "Wrong key usage: %d\n",
172
result->wrong_key_usage);
173
if(result->file_name != NULL){
174
fprintf(stderr, "File name: %s\n", result->file_name);
176
gpgme_recipient_t recipient;
177
recipient = result->recipients;
179
while(recipient != NULL){
180
fprintf(stderr, "Public key algorithm: %s\n",
181
gpgme_pubkey_algo_name(recipient->pubkey_algo));
182
fprintf(stderr, "Key ID: %s\n", recipient->keyid);
183
fprintf(stderr, "Secret key available: %s\n",
184
recipient->status == GPG_ERR_NO_SECKEY
186
recipient = recipient->next;
325
fprintf(stderr, "Decryption of OpenPGP data succeeded\n");
192
/* Delete the GPGME FILE pointer cryptotext data buffer */
193
gpgme_data_release(dh_crypto);
328
195
/* Seek back to the beginning of the GPGME plaintext data buffer */
329
if(gpgme_data_seek(dh_plain, (off_t)0, SEEK_SET) == -1){
330
perror("gpgme_data_seek");
331
plaintext_length = -1;
196
gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET);
337
plaintext_capacity = incbuffer(plaintext,
338
(size_t)plaintext_length,
340
if(plaintext_capacity == 0){
342
plaintext_length = -1;
200
if (new_packet_length + BUFFER_SIZE > new_packet_capacity){
201
*new_packet = realloc(*new_packet,
202
(unsigned int)new_packet_capacity
204
if (*new_packet == NULL){
208
new_packet_capacity += BUFFER_SIZE;
346
ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
211
ret = gpgme_data_read(dh_plain, *new_packet + new_packet_length,
348
213
/* Print the data, if any */
354
218
perror("gpgme_data_read");
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);
221
new_packet_length += ret;
224
/* FIXME: check characters before printing to screen so to not print
225
terminal control characters */
227
/* fprintf(stderr, "decrypted password is: "); */
228
/* fwrite(*new_packet, 1, new_packet_length, stderr); */
229
/* fprintf(stderr, "\n"); */
374
232
/* Delete the GPGME plaintext data buffer */
375
233
gpgme_data_release(dh_plain);
376
return plaintext_length;
234
return new_packet_length;
379
static const char * safer_gnutls_strerror(int value){
380
const char *ret = gnutls_strerror(value); /* Spurious warning from
381
-Wunreachable-code */
237
static const char * safer_gnutls_strerror (int value) {
238
const char *ret = gnutls_strerror (value);
383
240
ret = "(unknown)";
387
/* GnuTLS log function callback */
388
static void debuggnutls(__attribute__((unused)) int level,
390
fprintf(stderr, "GnuTLS: %s", string);
244
void debuggnutls(__attribute__((unused)) int level,
246
fprintf(stderr, "%s", string);
393
static int init_gnutls_global(const char *pubkeyfilename,
394
const char *seckeyfilename){
249
int initgnutls(encrypted_session *es){
398
254
fprintf(stderr, "Initializing GnuTLS\n");
401
ret = gnutls_global_init();
402
if(ret != GNUTLS_E_SUCCESS){
403
fprintf(stderr, "GnuTLS global_init: %s\n",
404
safer_gnutls_strerror(ret));
257
if ((ret = gnutls_global_init ())
258
!= GNUTLS_E_SUCCESS) {
259
fprintf (stderr, "global_init: %s\n", safer_gnutls_strerror(ret));
409
/* "Use a log level over 10 to enable all debugging options."
412
264
gnutls_global_set_log_level(11);
413
265
gnutls_global_set_log_function(debuggnutls);
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();
268
/* openpgp credentials */
269
if ((ret = gnutls_certificate_allocate_credentials (&es->cred))
270
!= GNUTLS_E_SUCCESS) {
271
fprintf (stderr, "memory error: %s\n",
272
safer_gnutls_strerror(ret));
429
fprintf(stderr, "Attempting to use OpenPGP public key %s and"
430
" secret key %s as GnuTLS credentials\n", pubkeyfilename,
277
fprintf(stderr, "Attempting to use OpenPGP certificate %s"
278
" and keyfile %s as GnuTLS credentials\n", CERTFILE,
434
282
ret = gnutls_certificate_set_openpgp_key_file
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){
283
(es->cred, CERTFILE, KEYFILE, GNUTLS_OPENPGP_FMT_BASE64);
284
if (ret != GNUTLS_E_SUCCESS) {
286
(stderr, "Error[%d] while reading the OpenPGP key pair ('%s',"
288
ret, CERTFILE, KEYFILE);
289
fprintf(stdout, "The Error is: %s\n",
290
safer_gnutls_strerror(ret));
294
//GnuTLS server initialization
295
if ((ret = gnutls_dh_params_init (&es->dh_params))
296
!= GNUTLS_E_SUCCESS) {
297
fprintf (stderr, "Error in dh parameter initialization: %s\n",
298
safer_gnutls_strerror(ret));
302
if ((ret = gnutls_dh_params_generate2 (es->dh_params, DH_BITS))
303
!= GNUTLS_E_SUCCESS) {
304
fprintf (stderr, "Error in prime generation: %s\n",
305
safer_gnutls_strerror(ret));
309
gnutls_certificate_set_dh_params (es->cred, es->dh_params);
311
// GnuTLS session creation
312
if ((ret = gnutls_init (&es->session, GNUTLS_SERVER))
313
!= GNUTLS_E_SUCCESS){
477
314
fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
478
315
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);
318
if ((ret = gnutls_priority_set_direct (es->session, "NORMAL", &err))
319
!= GNUTLS_E_SUCCESS) {
320
fprintf(stderr, "Syntax error at: %s\n", err);
321
fprintf(stderr, "GnuTLS error: %s\n",
322
safer_gnutls_strerror(ret));
493
ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
495
if(ret != GNUTLS_E_SUCCESS){
496
fprintf(stderr, "Error setting GnuTLS credentials: %s\n",
326
if ((ret = gnutls_credentials_set
327
(es->session, GNUTLS_CRD_CERTIFICATE, es->cred))
328
!= GNUTLS_E_SUCCESS) {
329
fprintf(stderr, "Error setting a credentials set: %s\n",
497
330
safer_gnutls_strerror(ret));
498
gnutls_deinit(*session);
502
334
/* ignore client certificate if any. */
503
gnutls_certificate_server_set_request(*session,
335
gnutls_certificate_server_set_request (es->session,
506
gnutls_dh_set_prime_bits(*session, mc.dh_bits);
338
gnutls_dh_set_prime_bits (es->session, DH_BITS);
511
/* Avahi log function callback */
512
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
513
__attribute__((unused)) const char *txt){}
343
void empty_log(__attribute__((unused)) AvahiLogLevel level,
344
__attribute__((unused)) const char *txt){}
515
/* Called when a Mandos server is found */
516
static int start_mandos_communication(const char *ip, uint16_t port,
517
AvahiIfIndex if_index,
346
int start_mandos_communication(const char *ip, uint16_t port,
347
unsigned int if_index){
522
struct sockaddr_in in;
523
struct sockaddr_in6 in6;
349
struct sockaddr_in6 to;
350
encrypted_session es;
525
351
char *buffer = NULL;
526
352
char *decrypted_buffer;
527
353
size_t buffer_length = 0;
528
354
size_t buffer_capacity = 0;
529
355
ssize_t decrypted_buffer_size;
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);
358
char interface[IF_NAMESIZE];
553
fprintf(stderr, "Setting up a TCP connection to %s, port %" PRIu16
361
fprintf(stderr, "Setting up a tcp connection to %s\n", ip);
557
tcp_sd = socket(pf, SOCK_STREAM, 0);
364
tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
559
366
perror("socket");
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);
370
if(if_indextoname(if_index, interface) == NULL){
372
perror("if_indextoname");
378
fprintf(stderr, "Binding to interface %s\n", interface);
381
memset(&to,0,sizeof(to)); /* Spurious warning */
382
to.sin6_family = AF_INET6;
383
ret = inet_pton(AF_INET6, ip, &to.sin6_addr);
572
385
perror("inet_pton");
576
389
fprintf(stderr, "Bad address: %s\n", ip);
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 */
392
to.sin6_port = htons(port); /* Spurious warning */
394
to.sin6_scope_id = (uint32_t)if_index;
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);
397
fprintf(stderr, "Connection to: %s\n", ip);
634
ret = connect(tcp_sd, &to.in6, sizeof(to));
636
ret = connect(tcp_sd, &to.in, sizeof(to)); /* IPv4 */
400
ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
639
402
perror("connect");
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){
406
ret = initgnutls (&es);
412
gnutls_transport_set_ptr (es.session,
413
(gnutls_transport_ptr_t) tcp_sd);
668
416
fprintf(stderr, "Establishing TLS session with %s\n", ip);
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){
419
ret = gnutls_handshake (es.session);
421
if (ret != GNUTLS_E_SUCCESS){
679
fprintf(stderr, "*** GnuTLS Handshake failed ***\n");
423
fprintf(stderr, "\n*** Handshake failed ***\n");
686
/* Read OpenPGP packet that contains the wanted password */
430
//Retrieve OpenPGP packet that contains the wanted password
689
fprintf(stderr, "Retrieving OpenPGP encrypted password from %s\n",
433
fprintf(stderr, "Retrieving pgp encrypted password from %s\n",
694
buffer_capacity = incbuffer(&buffer, buffer_length,
696
if(buffer_capacity == 0){
438
if (buffer_length + BUFFER_SIZE > buffer_capacity){
439
buffer = realloc(buffer, buffer_capacity + BUFFER_SIZE);
444
buffer_capacity += BUFFER_SIZE;
702
sret = gnutls_record_recv(session, buffer+buffer_length,
447
ret = gnutls_record_recv
448
(es.session, buffer+buffer_length, BUFFER_SIZE);
709
454
case GNUTLS_E_INTERRUPTED:
710
455
case GNUTLS_E_AGAIN:
712
457
case GNUTLS_E_REHANDSHAKE:
714
ret = gnutls_handshake(session);
715
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
717
fprintf(stderr, "*** GnuTLS Re-handshake failed ***\n");
458
ret = gnutls_handshake (es.session);
460
fprintf(stderr, "\n*** Handshake failed ***\n");
724
467
fprintf(stderr, "Unknown error while reading data from"
725
" encrypted session with Mandos server\n");
468
" encrypted session with mandos server\n");
727
gnutls_bye(session, GNUTLS_SHUT_RDWR);
470
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
731
buffer_length += (size_t) sret;
474
buffer_length += (size_t) ret;
736
fprintf(stderr, "Closing TLS session\n");
739
gnutls_bye(session, GNUTLS_SHUT_RDWR);
741
if(buffer_length > 0){
478
if (buffer_length > 0){
742
479
decrypted_buffer_size = pgp_packet_decrypt(buffer,
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,
483
if (decrypted_buffer_size >= 0){
484
while(written < decrypted_buffer_size){
485
ret = (int)fwrite (decrypted_buffer + written, 1,
486
(size_t)decrypted_buffer_size - written,
751
488
if(ret == 0 and ferror(stdout)){
753
490
fprintf(stderr, "Error writing encrypted data: %s\n",
810
553
char ip[AVAHI_ADDRESS_STR_MAX];
811
554
avahi_address_snprint(ip, sizeof(ip), address);
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);
556
fprintf(stderr, "Mandos server \"%s\" found on %s (%s) on"
557
" port %d\n", name, host_name, 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);
559
int ret = start_mandos_communication(ip, port,
560
(unsigned int) interface);
824
566
avahi_s_service_resolver_free(r);
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,
858
protocol, name, type, domain,
859
AVAHI_PROTO_INET6, 0,
860
resolve_callback, NULL)))
861
fprintf(stderr, "Avahi: Failed to resolve service '%s': %s\n",
862
name, avahi_strerror(avahi_server_errno(mc.server)));
865
case AVAHI_BROWSER_REMOVE:
868
case AVAHI_BROWSER_ALL_FOR_NOW:
869
case AVAHI_BROWSER_CACHE_EXHAUSTED:
871
fprintf(stderr, "No Mandos server found, still searching...\n");
877
sig_atomic_t quit_now = 0;
879
/* stop main loop after sigterm has been called */
880
static void handle_sigterm(__attribute__((unused)) int sig){
885
int old_errno = errno;
886
if(mc.simple_poll != NULL){
887
avahi_simple_poll_quit(mc.simple_poll);
892
int main(int argc, char *argv[]){
893
AvahiSServiceBrowser *sb = NULL;
898
int exitcode = EXIT_SUCCESS;
899
const char *interface = "eth0";
900
struct ifreq network;
904
char *connect_to = NULL;
905
char tempdir[] = "/tmp/mandosXXXXXX";
906
bool tempdir_created = false;
907
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
908
const char *seckey = PATHDIR "/" SECKEY;
909
const char *pubkey = PATHDIR "/" PUBKEY;
911
/* Initialize Mandos context */
912
mc = (mandos_context){ .simple_poll = NULL, .server = NULL,
913
.dh_bits = 1024, .priority = "SECURE256"
914
":!CTYPE-X.509:+CTYPE-OPENPGP" };
915
bool gnutls_initialized = false;
916
bool gpgme_initialized = false;
919
struct sigaction old_sigterm_action;
920
struct sigaction sigterm_action = { .sa_handler = handle_sigterm };
923
struct argp_option options[] = {
924
{ .name = "debug", .key = 128,
925
.doc = "Debug mode", .group = 3 },
926
{ .name = "connect", .key = 'c',
927
.arg = "ADDRESS:PORT",
928
.doc = "Connect directly to a specific Mandos server",
930
{ .name = "interface", .key = 'i',
932
.doc = "Network interface that will be used to search for"
935
{ .name = "seckey", .key = 's',
937
.doc = "OpenPGP secret key file base name",
939
{ .name = "pubkey", .key = 'p',
941
.doc = "OpenPGP public key file base name",
943
{ .name = "dh-bits", .key = 129,
945
.doc = "Bit length of the prime number used in the"
946
" Diffie-Hellman key exchange",
948
{ .name = "priority", .key = 130,
950
.doc = "GnuTLS priority string for the TLS handshake",
952
{ .name = "delay", .key = 131,
954
.doc = "Maximum delay to wait for interface startup",
959
error_t parse_opt(int key, char *arg,
960
struct argp_state *state){
962
case 128: /* --debug */
965
case 'c': /* --connect */
968
case 'i': /* --interface */
971
case 's': /* --seckey */
974
case 'p': /* --pubkey */
977
case 129: /* --dh-bits */
979
tmpmax = strtoimax(arg, &tmp, 10);
980
if(errno != 0 or tmp == arg or *tmp != '\0'
981
or tmpmax != (typeof(mc.dh_bits))tmpmax){
982
fprintf(stderr, "Bad number of DH bits\n");
985
mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
987
case 130: /* --priority */
990
case 131: /* --delay */
992
delay = strtof(arg, &tmp);
993
if(errno != 0 or tmp == arg or *tmp != '\0'){
994
fprintf(stderr, "Bad delay\n");
1003
return ARGP_ERR_UNKNOWN;
1008
struct argp argp = { .options = options, .parser = parse_opt,
1010
.doc = "Mandos client -- Get and decrypt"
1011
" passwords from a Mandos server" };
1012
ret = argp_parse(&argp, argc, argv, 0, 0, NULL);
1013
if(ret == ARGP_ERR_UNKNOWN){
1014
fprintf(stderr, "Unknown error while parsing arguments\n");
1015
exitcode = EXIT_FAILURE;
1021
avahi_set_log_function(empty_log);
1024
/* Initialize Avahi early so avahi_simple_poll_quit() can be called
1025
from the signal handler */
1026
/* Initialize the pseudo-RNG for Avahi */
1027
srand((unsigned int) time(NULL));
1028
mc.simple_poll = avahi_simple_poll_new();
1029
if(mc.simple_poll == NULL){
1030
fprintf(stderr, "Avahi: Failed to create simple poll object.\n");
1031
exitcode = EXIT_FAILURE;
1035
sigemptyset(&sigterm_action.sa_mask);
1036
ret = sigaddset(&sigterm_action.sa_mask, SIGINT);
1038
perror("sigaddset");
1039
exitcode = EXIT_FAILURE;
1042
ret = sigaddset(&sigterm_action.sa_mask, SIGHUP);
1044
perror("sigaddset");
1045
exitcode = EXIT_FAILURE;
1048
ret = sigaddset(&sigterm_action.sa_mask, SIGTERM);
1050
perror("sigaddset");
1051
exitcode = EXIT_FAILURE;
1054
ret = sigaction(SIGTERM, &sigterm_action, &old_sigterm_action);
1056
perror("sigaction");
1057
exitcode = EXIT_FAILURE;
1061
/* If the interface is down, bring it up */
1062
if(interface[0] != '\0'){
1064
/* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
1065
messages to mess up the prompt */
1066
ret = klogctl(8, NULL, 5);
1067
bool restore_loglevel = true;
1069
restore_loglevel = false;
1072
#endif /* __linux__ */
1074
sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1077
exitcode = EXIT_FAILURE;
1079
if(restore_loglevel){
1080
ret = klogctl(7, NULL, 0);
1085
#endif /* __linux__ */
1088
strcpy(network.ifr_name, interface);
1089
ret = ioctl(sd, SIOCGIFFLAGS, &network);
1091
perror("ioctl SIOCGIFFLAGS");
1093
if(restore_loglevel){
1094
ret = klogctl(7, NULL, 0);
1099
#endif /* __linux__ */
1100
exitcode = EXIT_FAILURE;
1103
if((network.ifr_flags & IFF_UP) == 0){
1104
network.ifr_flags |= IFF_UP;
1105
ret = ioctl(sd, SIOCSIFFLAGS, &network);
1107
perror("ioctl SIOCSIFFLAGS");
1108
exitcode = EXIT_FAILURE;
1110
if(restore_loglevel){
1111
ret = klogctl(7, NULL, 0);
1116
#endif /* __linux__ */
1120
/* sleep checking until interface is running */
1121
for(int i=0; i < delay * 4; i++){
1122
ret = ioctl(sd, SIOCGIFFLAGS, &network);
1124
perror("ioctl SIOCGIFFLAGS");
1125
} else if(network.ifr_flags & IFF_RUNNING){
1128
struct timespec sleeptime = { .tv_nsec = 250000000 };
1129
ret = nanosleep(&sleeptime, NULL);
1130
if(ret == -1 and errno != EINTR){
1131
perror("nanosleep");
1134
ret = (int)TEMP_FAILURE_RETRY(close(sd));
1139
if(restore_loglevel){
1140
/* Restores kernel loglevel to default */
1141
ret = klogctl(7, NULL, 0);
1146
#endif /* __linux__ */
1163
ret = init_gnutls_global(pubkey, seckey);
1165
fprintf(stderr, "init_gnutls_global failed\n");
1166
exitcode = EXIT_FAILURE;
1169
gnutls_initialized = true;
1172
if(mkdtemp(tempdir) == NULL){
1176
tempdir_created = true;
1178
if(not init_gpgme(pubkey, seckey, tempdir)){
1179
fprintf(stderr, "init_gpgme failed\n");
1180
exitcode = EXIT_FAILURE;
1183
gpgme_initialized = true;
1186
if(interface[0] != '\0'){
1187
if_index = (AvahiIfIndex) if_nametoindex(interface);
1189
fprintf(stderr, "No such interface: \"%s\"\n", interface);
1190
exitcode = EXIT_FAILURE;
1195
if(connect_to != NULL){
1196
/* Connect directly, do not use Zeroconf */
1197
/* (Mainly meant for debugging) */
1198
char *address = strrchr(connect_to, ':');
1199
if(address == NULL){
1200
fprintf(stderr, "No colon in address\n");
1201
exitcode = EXIT_FAILURE;
1206
tmpmax = strtoimax(address+1, &tmp, 10);
1207
if(errno != 0 or tmp == address+1 or *tmp != '\0'
1208
or tmpmax != (uint16_t)tmpmax){
1209
fprintf(stderr, "Bad port number\n");
1210
exitcode = EXIT_FAILURE;
1213
port = (uint16_t)tmpmax;
1215
address = connect_to;
1216
/* Colon in address indicates IPv6 */
1218
if(strchr(address, ':') != NULL){
1223
ret = start_mandos_communication(address, port, if_index, af);
1225
exitcode = EXIT_FAILURE;
1227
exitcode = EXIT_SUCCESS;
569
static void browse_callback(
570
AvahiSServiceBrowser *b,
571
AvahiIfIndex interface,
572
AvahiProtocol protocol,
573
AvahiBrowserEvent event,
577
AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
580
AvahiServer *s = userdata;
581
assert(b); /* Spurious warning */
583
/* Called whenever a new services becomes available on the LAN or
584
is removed from the LAN */
588
case AVAHI_BROWSER_FAILURE:
590
fprintf(stderr, "(Browser) %s\n",
591
avahi_strerror(avahi_server_errno(server)));
592
avahi_simple_poll_quit(simple_poll);
595
case AVAHI_BROWSER_NEW:
596
/* We ignore the returned resolver object. In the callback
597
function we free it. If the server is terminated before
598
the callback function is called the server will free
599
the resolver for us. */
601
if (!(avahi_s_service_resolver_new(s, interface, protocol, name,
603
AVAHI_PROTO_INET6, 0,
604
resolve_callback, s)))
605
fprintf(stderr, "Failed to resolve service '%s': %s\n", name,
606
avahi_strerror(avahi_server_errno(s)));
609
case AVAHI_BROWSER_REMOVE:
612
case AVAHI_BROWSER_ALL_FOR_NOW:
613
case AVAHI_BROWSER_CACHE_EXHAUSTED:
618
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
1233
619
AvahiServerConfig config;
1234
/* Do not publish any local Zeroconf records */
620
AvahiSServiceBrowser *sb = NULL;
623
int returncode = EXIT_SUCCESS;
624
const char *interface = "eth0";
627
static struct option long_options[] = {
628
{"debug", no_argument, (int *)&debug, 1},
629
{"interface", required_argument, 0, 'i'},
632
int option_index = 0;
633
ret = getopt_long (argc, argv, "i:", long_options,
652
avahi_set_log_function(empty_log);
655
/* Initialize the psuedo-RNG */
656
srand((unsigned int) time(NULL));
658
/* Allocate main loop object */
659
if (!(simple_poll = avahi_simple_poll_new())) {
660
fprintf(stderr, "Failed to create simple poll object.\n");
665
/* Do not publish any local records */
1235
666
avahi_server_config_init(&config);
1236
667
config.publish_hinfo = 0;
1237
668
config.publish_addresses = 0;
1238
669
config.publish_workstation = 0;
1239
670
config.publish_domain = 0;
1241
672
/* Allocate a new server */
1242
mc.server = avahi_server_new(avahi_simple_poll_get
1243
(mc.simple_poll), &config, NULL,
1246
/* Free the Avahi configuration data */
673
server = avahi_server_new(avahi_simple_poll_get(simple_poll),
674
&config, NULL, NULL, &error);
676
/* Free the configuration data */
1247
677
avahi_server_config_free(&config);
1250
/* Check if creating the Avahi server object succeeded */
1251
if(mc.server == NULL){
1252
fprintf(stderr, "Failed to create Avahi server: %s\n",
1253
avahi_strerror(error));
1254
exitcode = EXIT_FAILURE;
1258
/* Create the Avahi service browser */
1259
sb = avahi_s_service_browser_new(mc.server, if_index,
1260
AVAHI_PROTO_INET6, "_mandos._tcp",
1261
NULL, 0, browse_callback, NULL);
1263
fprintf(stderr, "Failed to create service browser: %s\n",
1264
avahi_strerror(avahi_server_errno(mc.server)));
1265
exitcode = EXIT_FAILURE;
1269
/* Run the main loop */
1272
fprintf(stderr, "Starting Avahi loop search\n");
1275
avahi_simple_poll_loop(mc.simple_poll);
1280
fprintf(stderr, "%s exiting\n", argv[0]);
1283
/* Cleanup things */
1285
avahi_s_service_browser_free(sb);
1287
if(mc.server != NULL)
1288
avahi_server_free(mc.server);
1290
if(mc.simple_poll != NULL)
1291
avahi_simple_poll_free(mc.simple_poll);
1293
if(gnutls_initialized){
1294
gnutls_certificate_free_credentials(mc.cred);
1295
gnutls_global_deinit();
1296
gnutls_dh_params_deinit(mc.dh_params);
1299
if(gpgme_initialized){
1300
gpgme_release(mc.ctx);
1303
/* Removes the temp directory used by GPGME */
1304
if(tempdir_created){
1306
struct dirent *direntry;
1307
d = opendir(tempdir);
1309
if(errno != ENOENT){
1314
direntry = readdir(d);
1315
if(direntry == NULL){
1318
/* Skip "." and ".." */
1319
if(direntry->d_name[0] == '.'
1320
and (direntry->d_name[1] == '\0'
1321
or (direntry->d_name[1] == '.'
1322
and direntry->d_name[2] == '\0'))){
1325
char *fullname = NULL;
1326
ret = asprintf(&fullname, "%s/%s", tempdir,
1332
ret = remove(fullname);
1334
fprintf(stderr, "remove(\"%s\"): %s\n", fullname,
1341
ret = rmdir(tempdir);
1342
if(ret == -1 and errno != ENOENT){
679
/* Check if creating the server object succeeded */
681
fprintf(stderr, "Failed to create server: %s\n",
682
avahi_strerror(error));
683
returncode = EXIT_FAILURE;
687
/* Create the service browser */
688
sb = avahi_s_service_browser_new(server,
690
if_nametoindex(interface),
692
"_mandos._tcp", NULL, 0,
693
browse_callback, server);
695
fprintf(stderr, "Failed to create service browser: %s\n",
696
avahi_strerror(avahi_server_errno(server)));
697
returncode = EXIT_FAILURE;
701
/* Run the main loop */
704
fprintf(stderr, "Starting avahi loop search\n");
707
avahi_simple_poll_loop(simple_poll);
712
fprintf(stderr, "%s exiting\n", argv[0]);
717
avahi_s_service_browser_free(sb);
720
avahi_server_free(server);
723
avahi_simple_poll_free(simple_poll);