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(), sscanf(),
41
#include <stdint.h> /* uint16_t, uint32_t */
42
#include <stddef.h> /* NULL, size_t, ssize_t */
43
#include <stdlib.h> /* free(), EXIT_SUCCESS, EXIT_FAILURE,
45
#include <stdbool.h> /* bool, true */
46
#include <string.h> /* memset(), strcmp(), strlen(),
47
strerror(), asprintf(), strcpy() */
48
#include <sys/ioctl.h> /* ioctl */
49
#include <sys/types.h> /* socket(), inet_pton(), sockaddr,
50
sockaddr_in6, PF_INET6,
51
SOCK_STREAM, INET6_ADDRSTRLEN,
52
uid_t, gid_t, open(), opendir(),
54
#include <sys/stat.h> /* open() */
55
#include <sys/socket.h> /* socket(), struct sockaddr_in6,
56
struct in6_addr, inet_pton(),
58
#include <fcntl.h> /* open() */
59
#include <dirent.h> /* opendir(), struct dirent, readdir()
61
#include <inttypes.h> /* PRIu16, intmax_t, SCNdMAX */
62
#include <assert.h> /* assert() */
63
#include <errno.h> /* perror(), errno */
64
#include <time.h> /* time() */
65
#include <net/if.h> /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
66
SIOCSIFFLAGS, if_indextoname(),
67
if_nametoindex(), IF_NAMESIZE */
68
#include <netinet/in.h>
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, and, or */
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 */
80
/* All Avahi types, constants and functions
41
#include <net/if.h> /* if_nametoindex */
83
43
#include <avahi-core/core.h>
84
44
#include <avahi-core/lookup.h>
85
45
#include <avahi-core/log.h>
87
47
#include <avahi-common/malloc.h>
88
48
#include <avahi-common/error.h>
91
#include <gnutls/gnutls.h> /* All GnuTLS types, constants and
94
init_gnutls_session(),
96
#include <gnutls/openpgp.h>
97
/* gnutls_certificate_set_openpgp_key_file(),
98
GNUTLS_OPENPGP_FMT_BASE64 */
101
#include <gpgme.h> /* All GPGME types, constants and
104
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() */
107
71
#define BUFFER_SIZE 256
109
#define PATHDIR "/conf/conf.d/mandos"
110
#define SECKEY "seckey.txt"
111
#define PUBKEY "pubkey.txt"
74
const char *certdir = "/conf/conf.d/cryptkeyreq/";
75
const char *certfile = "openpgp-client.txt";
76
const char *certkey = "openpgp-client-key.txt";
113
78
bool debug = false;
114
static const char mandos_protocol_version[] = "1";
115
const char *argp_program_version = "mandos-client " VERSION;
116
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
118
/* Used for passing in values through the Avahi callback functions */
120
AvahiSimplePoll *simple_poll;
81
gnutls_session_t session;
122
82
gnutls_certificate_credentials_t cred;
123
unsigned int dh_bits;
124
83
gnutls_dh_params_t dh_params;
125
const char *priority;
87
ssize_t pgp_packet_decrypt (char *packet, size_t packet_size,
88
char **new_packet, const char *homedir){
89
gpgme_data_t dh_crypto, dh_plain;
130
* Make room in "buffer" for at least BUFFER_SIZE additional bytes.
131
* "buffer_capacity" is how much is currently allocated,
132
* "buffer_length" is how much is already used.
134
size_t adjustbuffer(char **buffer, size_t buffer_length,
135
size_t buffer_capacity){
136
if(buffer_length + BUFFER_SIZE > buffer_capacity){
137
*buffer = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
141
buffer_capacity += BUFFER_SIZE;
143
return buffer_capacity;
149
static bool init_gpgme(mandos_context *mc, const char *seckey,
150
const char *pubkey, const char *tempdir){
93
ssize_t new_packet_capacity = 0;
94
ssize_t new_packet_length = 0;
153
95
gpgme_engine_info_t engine_info;
157
* Helper function to insert pub and seckey to the enigne keyring.
159
bool import_key(const char *filename){
161
gpgme_data_t pgp_data;
163
fd = (int)TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
169
rc = gpgme_data_new_from_fd(&pgp_data, fd);
170
if(rc != GPG_ERR_NO_ERROR){
171
fprintf(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
172
gpgme_strsource(rc), gpgme_strerror(rc));
176
rc = gpgme_op_import(mc->ctx, pgp_data);
177
if(rc != GPG_ERR_NO_ERROR){
178
fprintf(stderr, "bad gpgme_op_import: %s: %s\n",
179
gpgme_strsource(rc), gpgme_strerror(rc));
183
ret = (int)TEMP_FAILURE_RETRY(close(fd));
187
gpgme_data_release(pgp_data);
192
fprintf(stderr, "Initialize gpgme\n");
98
fprintf(stderr, "Trying to decrypt OpenPGP packet\n");
196
102
gpgme_check_version(NULL);
197
103
rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
198
if(rc != GPG_ERR_NO_ERROR){
104
if (rc != GPG_ERR_NO_ERROR){
199
105
fprintf(stderr, "bad gpgme_engine_check_version: %s: %s\n",
200
106
gpgme_strsource(rc), gpgme_strerror(rc));
204
/* Set GPGME home directory for the OpenPGP engine only */
205
rc = gpgme_get_engine_info(&engine_info);
206
if(rc != GPG_ERR_NO_ERROR){
110
/* Set GPGME home directory */
111
rc = gpgme_get_engine_info (&engine_info);
112
if (rc != GPG_ERR_NO_ERROR){
207
113
fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
208
114
gpgme_strsource(rc), gpgme_strerror(rc));
211
117
while(engine_info != NULL){
212
118
if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
213
119
gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
214
engine_info->file_name, tempdir);
120
engine_info->file_name, homedir);
217
123
engine_info = engine_info->next;
219
125
if(engine_info == NULL){
220
fprintf(stderr, "Could not set GPGME home dir to %s\n", tempdir);
224
/* Create new GPGME "context" */
225
rc = gpgme_new(&(mc->ctx));
226
if(rc != GPG_ERR_NO_ERROR){
227
fprintf(stderr, "bad gpgme_new: %s: %s\n",
228
gpgme_strsource(rc), gpgme_strerror(rc));
232
if(not import_key(pubkey) or not import_key(seckey)){
240
* Decrypt OpenPGP data.
241
* Returns -1 on error
243
static ssize_t pgp_packet_decrypt(const mandos_context *mc,
244
const char *cryptotext,
247
gpgme_data_t dh_crypto, dh_plain;
250
size_t plaintext_capacity = 0;
251
ssize_t plaintext_length = 0;
254
fprintf(stderr, "Trying to decrypt OpenPGP data\n");
257
/* Create new GPGME data buffer from memory cryptotext */
258
rc = gpgme_data_new_from_mem(&dh_crypto, cryptotext, crypto_size,
260
if(rc != GPG_ERR_NO_ERROR){
126
fprintf(stderr, "Could not set home dir to %s\n", homedir);
130
/* Create new GPGME data buffer from packet buffer */
131
rc = gpgme_data_new_from_mem(&dh_crypto, packet, packet_size, 0);
132
if (rc != GPG_ERR_NO_ERROR){
261
133
fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
262
134
gpgme_strsource(rc), gpgme_strerror(rc));
266
138
/* Create new empty GPGME data buffer for the plaintext */
267
139
rc = gpgme_data_new(&dh_plain);
268
if(rc != GPG_ERR_NO_ERROR){
140
if (rc != GPG_ERR_NO_ERROR){
269
141
fprintf(stderr, "bad gpgme_data_new: %s: %s\n",
270
142
gpgme_strsource(rc), gpgme_strerror(rc));
271
gpgme_data_release(dh_crypto);
275
/* Decrypt data from the cryptotext data buffer to the plaintext
277
rc = gpgme_op_decrypt(mc->ctx, dh_crypto, dh_plain);
278
if(rc != GPG_ERR_NO_ERROR){
146
/* Create new GPGME "context" */
147
rc = gpgme_new(&ctx);
148
if (rc != GPG_ERR_NO_ERROR){
149
fprintf(stderr, "bad gpgme_new: %s: %s\n",
150
gpgme_strsource(rc), gpgme_strerror(rc));
154
/* Decrypt data from the FILE pointer to the plaintext data
156
rc = gpgme_op_decrypt(ctx, dh_crypto, dh_plain);
157
if (rc != GPG_ERR_NO_ERROR){
279
158
fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
280
159
gpgme_strsource(rc), gpgme_strerror(rc));
281
plaintext_length = -1;
283
gpgme_decrypt_result_t result;
284
result = gpgme_op_decrypt_result(mc->ctx);
286
fprintf(stderr, "gpgme_op_decrypt_result failed\n");
288
fprintf(stderr, "Unsupported algorithm: %s\n",
289
result->unsupported_algorithm);
290
fprintf(stderr, "Wrong key usage: %u\n",
291
result->wrong_key_usage);
292
if(result->file_name != NULL){
293
fprintf(stderr, "File name: %s\n", result->file_name);
295
gpgme_recipient_t recipient;
296
recipient = result->recipients;
298
while(recipient != NULL){
299
fprintf(stderr, "Public key algorithm: %s\n",
300
gpgme_pubkey_algo_name(recipient->pubkey_algo));
301
fprintf(stderr, "Key ID: %s\n", recipient->keyid);
302
fprintf(stderr, "Secret key available: %s\n",
303
recipient->status == GPG_ERR_NO_SECKEY
305
recipient = recipient->next;
164
fprintf(stderr, "Decryption of OpenPGP packet succeeded\n");
168
gpgme_decrypt_result_t result;
169
result = gpgme_op_decrypt_result(ctx);
171
fprintf(stderr, "gpgme_op_decrypt_result failed\n");
173
fprintf(stderr, "Unsupported algorithm: %s\n",
174
result->unsupported_algorithm);
175
fprintf(stderr, "Wrong key usage: %d\n",
176
result->wrong_key_usage);
177
if(result->file_name != NULL){
178
fprintf(stderr, "File name: %s\n", result->file_name);
180
gpgme_recipient_t recipient;
181
recipient = result->recipients;
183
while(recipient != NULL){
184
fprintf(stderr, "Public key algorithm: %s\n",
185
gpgme_pubkey_algo_name(recipient->pubkey_algo));
186
fprintf(stderr, "Key ID: %s\n", recipient->keyid);
187
fprintf(stderr, "Secret key available: %s\n",
188
recipient->status == GPG_ERR_NO_SECKEY
190
recipient = recipient->next;
314
fprintf(stderr, "Decryption of OpenPGP data succeeded\n");
196
/* Delete the GPGME FILE pointer cryptotext data buffer */
197
gpgme_data_release(dh_crypto);
317
199
/* Seek back to the beginning of the GPGME plaintext data buffer */
318
if(gpgme_data_seek(dh_plain, (off_t)0, SEEK_SET) == -1){
319
perror("gpgme_data_seek");
320
plaintext_length = -1;
200
if (gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET) == -1){
201
perror("pgpme_data_seek");
326
plaintext_capacity = adjustbuffer(plaintext,
327
(size_t)plaintext_length,
329
if(plaintext_capacity == 0){
330
perror("adjustbuffer");
331
plaintext_length = -1;
206
if (new_packet_length + BUFFER_SIZE > new_packet_capacity){
207
*new_packet = realloc(*new_packet,
208
(unsigned int)new_packet_capacity
210
if (*new_packet == NULL){
214
new_packet_capacity += BUFFER_SIZE;
335
ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
217
ret = gpgme_data_read(dh_plain, *new_packet + new_packet_length,
337
219
/* Print the data, if any */
343
224
perror("gpgme_data_read");
344
plaintext_length = -1;
347
plaintext_length += ret;
351
fprintf(stderr, "Decrypted password is: ");
352
for(ssize_t i = 0; i < plaintext_length; i++){
353
fprintf(stderr, "%02hhX ", (*plaintext)[i]);
355
fprintf(stderr, "\n");
360
/* Delete the GPGME cryptotext data buffer */
361
gpgme_data_release(dh_crypto);
227
new_packet_length += ret;
230
/* FIXME: check characters before printing to screen so to not print
231
terminal control characters */
233
/* fprintf(stderr, "decrypted password is: "); */
234
/* fwrite(*new_packet, 1, new_packet_length, stderr); */
235
/* fprintf(stderr, "\n"); */
363
238
/* Delete the GPGME plaintext data buffer */
364
239
gpgme_data_release(dh_plain);
365
return plaintext_length;
240
return new_packet_length;
368
static const char * safer_gnutls_strerror(int value) {
369
const char *ret = gnutls_strerror(value); /* Spurious warning from
370
-Wunreachable-code */
243
static const char * safer_gnutls_strerror (int value) {
244
const char *ret = gnutls_strerror (value);
372
246
ret = "(unknown)";
376
/* GnuTLS log function callback */
377
static void debuggnutls(__attribute__((unused)) int level,
379
fprintf(stderr, "GnuTLS: %s", string);
250
void debuggnutls(__attribute__((unused)) int level,
252
fprintf(stderr, "%s", string);
382
static int init_gnutls_global(mandos_context *mc,
383
const char *pubkeyfilename,
384
const char *seckeyfilename){
255
int initgnutls(encrypted_session *es){
388
260
fprintf(stderr, "Initializing GnuTLS\n");
391
ret = gnutls_global_init();
392
if(ret != GNUTLS_E_SUCCESS) {
393
fprintf(stderr, "GnuTLS global_init: %s\n",
394
safer_gnutls_strerror(ret));
263
if ((ret = gnutls_global_init ())
264
!= GNUTLS_E_SUCCESS) {
265
fprintf (stderr, "global_init: %s\n", safer_gnutls_strerror(ret));
399
/* "Use a log level over 10 to enable all debugging options."
402
270
gnutls_global_set_log_level(11);
403
271
gnutls_global_set_log_function(debuggnutls);
406
/* OpenPGP credentials */
407
gnutls_certificate_allocate_credentials(&mc->cred);
408
if(ret != GNUTLS_E_SUCCESS){
409
fprintf(stderr, "GnuTLS memory error: %s\n", /* Spurious warning
413
safer_gnutls_strerror(ret));
414
gnutls_global_deinit();
274
/* openpgp credentials */
275
if ((ret = gnutls_certificate_allocate_credentials (&es->cred))
276
!= GNUTLS_E_SUCCESS) {
277
fprintf (stderr, "memory error: %s\n",
278
safer_gnutls_strerror(ret));
419
fprintf(stderr, "Attempting to use OpenPGP public key %s and"
420
" secret key %s as GnuTLS credentials\n", pubkeyfilename,
283
fprintf(stderr, "Attempting to use OpenPGP certificate %s"
284
" and keyfile %s as GnuTLS credentials\n", certfile,
424
288
ret = gnutls_certificate_set_openpgp_key_file
425
(mc->cred, pubkeyfilename, seckeyfilename,
426
GNUTLS_OPENPGP_FMT_BASE64);
427
if(ret != GNUTLS_E_SUCCESS) {
429
"Error[%d] while reading the OpenPGP key pair ('%s',"
430
" '%s')\n", ret, pubkeyfilename, seckeyfilename);
431
fprintf(stderr, "The GnuTLS error is: %s\n",
432
safer_gnutls_strerror(ret));
436
/* GnuTLS server initialization */
437
ret = gnutls_dh_params_init(&mc->dh_params);
438
if(ret != GNUTLS_E_SUCCESS) {
439
fprintf(stderr, "Error in GnuTLS DH parameter initialization:"
440
" %s\n", safer_gnutls_strerror(ret));
443
ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
444
if(ret != GNUTLS_E_SUCCESS) {
445
fprintf(stderr, "Error in GnuTLS prime generation: %s\n",
446
safer_gnutls_strerror(ret));
450
gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
456
gnutls_certificate_free_credentials(mc->cred);
457
gnutls_global_deinit();
458
gnutls_dh_params_deinit(mc->dh_params);
462
static int init_gnutls_session(mandos_context *mc,
463
gnutls_session_t *session){
465
/* GnuTLS session creation */
466
ret = gnutls_init(session, GNUTLS_SERVER);
467
if(ret != GNUTLS_E_SUCCESS){
289
(es->cred, certfile, certkey, GNUTLS_OPENPGP_FMT_BASE64);
290
if (ret != GNUTLS_E_SUCCESS) {
292
(stderr, "Error[%d] while reading the OpenPGP key pair ('%s',"
294
ret, certfile, certkey);
295
fprintf(stdout, "The Error is: %s\n",
296
safer_gnutls_strerror(ret));
300
//GnuTLS server initialization
301
if ((ret = gnutls_dh_params_init (&es->dh_params))
302
!= GNUTLS_E_SUCCESS) {
303
fprintf (stderr, "Error in dh parameter initialization: %s\n",
304
safer_gnutls_strerror(ret));
308
if ((ret = gnutls_dh_params_generate2 (es->dh_params, DH_BITS))
309
!= GNUTLS_E_SUCCESS) {
310
fprintf (stderr, "Error in prime generation: %s\n",
311
safer_gnutls_strerror(ret));
315
gnutls_certificate_set_dh_params (es->cred, es->dh_params);
317
// GnuTLS session creation
318
if ((ret = gnutls_init (&es->session, GNUTLS_SERVER))
319
!= GNUTLS_E_SUCCESS){
468
320
fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
469
321
safer_gnutls_strerror(ret));
474
ret = gnutls_priority_set_direct(*session, mc->priority, &err);
475
if(ret != GNUTLS_E_SUCCESS) {
476
fprintf(stderr, "Syntax error at: %s\n", err);
477
fprintf(stderr, "GnuTLS error: %s\n",
478
safer_gnutls_strerror(ret));
479
gnutls_deinit(*session);
324
if ((ret = gnutls_priority_set_direct (es->session, "NORMAL", &err))
325
!= GNUTLS_E_SUCCESS) {
326
fprintf(stderr, "Syntax error at: %s\n", err);
327
fprintf(stderr, "GnuTLS error: %s\n",
328
safer_gnutls_strerror(ret));
484
ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
486
if(ret != GNUTLS_E_SUCCESS) {
487
fprintf(stderr, "Error setting GnuTLS credentials: %s\n",
332
if ((ret = gnutls_credentials_set
333
(es->session, GNUTLS_CRD_CERTIFICATE, es->cred))
334
!= GNUTLS_E_SUCCESS) {
335
fprintf(stderr, "Error setting a credentials set: %s\n",
488
336
safer_gnutls_strerror(ret));
489
gnutls_deinit(*session);
493
340
/* ignore client certificate if any. */
494
gnutls_certificate_server_set_request(*session,
341
gnutls_certificate_server_set_request (es->session,
497
gnutls_dh_set_prime_bits(*session, mc->dh_bits);
344
gnutls_dh_set_prime_bits (es->session, DH_BITS);
502
/* Avahi log function callback */
503
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
504
__attribute__((unused)) const char *txt){}
349
void empty_log(__attribute__((unused)) AvahiLogLevel level,
350
__attribute__((unused)) const char *txt){}
506
/* Called when a Mandos server is found */
507
static int start_mandos_communication(const char *ip, uint16_t port,
508
AvahiIfIndex if_index,
352
int start_mandos_communication(const char *ip, uint16_t port,
353
unsigned int if_index){
512
union { struct sockaddr in; struct sockaddr_in6 in6; } to;
355
struct sockaddr_in6 to;
356
encrypted_session es;
513
357
char *buffer = NULL;
514
358
char *decrypted_buffer;
515
359
size_t buffer_length = 0;
516
360
size_t buffer_capacity = 0;
517
361
ssize_t decrypted_buffer_size;
520
364
char interface[IF_NAMESIZE];
521
gnutls_session_t session;
523
ret = init_gnutls_session(mc, &session);
529
fprintf(stderr, "Setting up a tcp connection to %s, port %" PRIu16
367
fprintf(stderr, "Setting up a tcp connection to %s\n", ip);
533
370
tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
540
if(if_indextoname((unsigned int)if_index, interface) == NULL){
376
if(if_indextoname(if_index, interface) == NULL){
541
378
perror("if_indextoname");
544
384
fprintf(stderr, "Binding to interface %s\n", interface);
547
memset(&to, 0, sizeof(to));
548
to.in6.sin6_family = AF_INET6;
549
/* It would be nice to have a way to detect if we were passed an
550
IPv4 address here. Now we assume an IPv6 address. */
551
ret = inet_pton(AF_INET6, ip, &to.in6.sin6_addr);
387
memset(&to,0,sizeof(to)); /* Spurious warning */
388
to.sin6_family = AF_INET6;
389
ret = inet_pton(AF_INET6, ip, &to.sin6_addr);
553
391
perror("inet_pton");
557
395
fprintf(stderr, "Bad address: %s\n", ip);
560
to.in6.sin6_port = htons(port); /* Spurious warnings from
562
-Wunreachable-code */
398
to.sin6_port = htons(port); /* Spurious warning */
564
to.in6.sin6_scope_id = (uint32_t)if_index;
400
to.sin6_scope_id = (uint32_t)if_index;
567
fprintf(stderr, "Connection to: %s, port %" PRIu16 "\n", ip,
569
char addrstr[INET6_ADDRSTRLEN] = "";
570
if(inet_ntop(to.in6.sin6_family, &(to.in6.sin6_addr), addrstr,
571
sizeof(addrstr)) == NULL){
574
if(strcmp(addrstr, ip) != 0){
575
fprintf(stderr, "Canonical address form: %s\n", addrstr);
403
fprintf(stderr, "Connection to: %s\n", ip);
580
ret = connect(tcp_sd, &to.in, sizeof(to));
406
ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
582
408
perror("connect");
586
const char *out = mandos_protocol_version;
589
size_t out_size = strlen(out);
590
ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
591
out_size - written));
597
written += (size_t)ret;
598
if(written < out_size){
601
if(out == mandos_protocol_version){
412
ret = initgnutls (&es);
418
gnutls_transport_set_ptr (es.session,
419
(gnutls_transport_ptr_t) tcp_sd);
611
422
fprintf(stderr, "Establishing TLS session with %s\n", ip);
614
gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t) tcp_sd);
617
ret = gnutls_handshake(session);
618
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
620
if(ret != GNUTLS_E_SUCCESS){
425
ret = gnutls_handshake (es.session);
427
if (ret != GNUTLS_E_SUCCESS){
622
fprintf(stderr, "*** GnuTLS Handshake failed ***\n");
429
fprintf(stderr, "\n*** Handshake failed ***\n");
629
/* Read OpenPGP packet that contains the wanted password */
436
//Retrieve OpenPGP packet that contains the wanted password
632
439
fprintf(stderr, "Retrieving pgp encrypted password from %s\n",
637
buffer_capacity = adjustbuffer(&buffer, buffer_length,
639
if(buffer_capacity == 0){
640
perror("adjustbuffer");
444
if (buffer_length + BUFFER_SIZE > buffer_capacity){
445
buffer = realloc(buffer, buffer_capacity + BUFFER_SIZE);
450
buffer_capacity += BUFFER_SIZE;
645
sret = gnutls_record_recv(session, buffer+buffer_length,
453
ret = gnutls_record_recv
454
(es.session, buffer+buffer_length, BUFFER_SIZE);
652
460
case GNUTLS_E_INTERRUPTED:
653
461
case GNUTLS_E_AGAIN:
655
463
case GNUTLS_E_REHANDSHAKE:
657
ret = gnutls_handshake(session);
658
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
660
fprintf(stderr, "*** GnuTLS Re-handshake failed ***\n");
464
ret = gnutls_handshake (es.session);
466
fprintf(stderr, "\n*** Handshake failed ***\n");
667
473
fprintf(stderr, "Unknown error while reading data from"
668
" encrypted session with Mandos server\n");
474
" encrypted session with mandos server\n");
670
gnutls_bye(session, GNUTLS_SHUT_RDWR);
476
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
674
buffer_length += (size_t) sret;
480
buffer_length += (size_t) ret;
679
fprintf(stderr, "Closing TLS session\n");
682
gnutls_bye(session, GNUTLS_SHUT_RDWR);
684
if(buffer_length > 0){
685
decrypted_buffer_size = pgp_packet_decrypt(mc, buffer,
484
if (buffer_length > 0){
485
decrypted_buffer_size = pgp_packet_decrypt(buffer,
688
if(decrypted_buffer_size >= 0){
690
while(written < (size_t) decrypted_buffer_size){
691
ret = (int)fwrite(decrypted_buffer + written, 1,
692
(size_t)decrypted_buffer_size - written,
489
if (decrypted_buffer_size >= 0){
490
while(written < decrypted_buffer_size){
491
ret = (int)fwrite (decrypted_buffer + written, 1,
492
(size_t)decrypted_buffer_size - written,
694
494
if(ret == 0 and ferror(stdout)){
696
496
fprintf(stderr, "Error writing encrypted data: %s\n",
754
559
char ip[AVAHI_ADDRESS_STR_MAX];
755
560
avahi_address_snprint(ip, sizeof(ip), address);
757
fprintf(stderr, "Mandos server \"%s\" found on %s (%s, %"
758
PRIdMAX ") on port %" PRIu16 "\n", name, host_name,
759
ip, (intmax_t)interface, port);
562
fprintf(stderr, "Mandos server \"%s\" found on %s (%s) on"
563
" port %d\n", name, host_name, ip, port);
761
int ret = start_mandos_communication(ip, port, interface, mc);
763
avahi_simple_poll_quit(mc->simple_poll);
565
int ret = start_mandos_communication(ip, port,
566
(unsigned int) interface);
767
572
avahi_s_service_resolver_free(r);
770
static void browse_callback( AvahiSServiceBrowser *b,
771
AvahiIfIndex interface,
772
AvahiProtocol protocol,
773
AvahiBrowserEvent event,
777
AVAHI_GCC_UNUSED AvahiLookupResultFlags
780
mandos_context *mc = userdata;
783
/* Called whenever a new services becomes available on the LAN or
784
is removed from the LAN */
788
case AVAHI_BROWSER_FAILURE:
790
fprintf(stderr, "(Avahi browser) %s\n",
791
avahi_strerror(avahi_server_errno(mc->server)));
792
avahi_simple_poll_quit(mc->simple_poll);
795
case AVAHI_BROWSER_NEW:
796
/* We ignore the returned Avahi resolver object. In the callback
797
function we free it. If the Avahi server is terminated before
798
the callback function is called the Avahi server will free the
801
if(!(avahi_s_service_resolver_new(mc->server, interface,
802
protocol, name, type, domain,
803
AVAHI_PROTO_INET6, 0,
804
resolve_callback, mc)))
805
fprintf(stderr, "Avahi: Failed to resolve service '%s': %s\n",
806
name, avahi_strerror(avahi_server_errno(mc->server)));
809
case AVAHI_BROWSER_REMOVE:
812
case AVAHI_BROWSER_ALL_FOR_NOW:
813
case AVAHI_BROWSER_CACHE_EXHAUSTED:
815
fprintf(stderr, "No Mandos server found, still searching...\n");
575
static void browse_callback(
576
AvahiSServiceBrowser *b,
577
AvahiIfIndex interface,
578
AvahiProtocol protocol,
579
AvahiBrowserEvent event,
583
AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
586
AvahiServer *s = userdata;
587
assert(b); /* Spurious warning */
589
/* Called whenever a new services becomes available on the LAN or
590
is removed from the LAN */
594
case AVAHI_BROWSER_FAILURE:
596
fprintf(stderr, "(Browser) %s\n",
597
avahi_strerror(avahi_server_errno(server)));
598
avahi_simple_poll_quit(simple_poll);
601
case AVAHI_BROWSER_NEW:
602
/* We ignore the returned resolver object. In the callback
603
function we free it. If the server is terminated before
604
the callback function is called the server will free
605
the resolver for us. */
607
if (!(avahi_s_service_resolver_new(s, interface, protocol, name,
609
AVAHI_PROTO_INET6, 0,
610
resolve_callback, s)))
611
fprintf(stderr, "Failed to resolve service '%s': %s\n", name,
612
avahi_strerror(avahi_server_errno(s)));
615
case AVAHI_BROWSER_REMOVE:
618
case AVAHI_BROWSER_ALL_FOR_NOW:
619
case AVAHI_BROWSER_CACHE_EXHAUSTED:
821
int main(int argc, char *argv[]){
624
/* combinds file name and path and returns the malloced new string. som sane checks could/should be added */
625
const char *combinepath(const char *first, const char *second){
627
tmp = malloc(strlen(first) + strlen(second) + 2);
633
if (first[0] != '\0' and first[strlen(first) - 1] != '/'){
641
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
642
AvahiServerConfig config;
822
643
AvahiSServiceBrowser *sb = NULL;
827
int exitcode = EXIT_SUCCESS;
646
int returncode = EXIT_SUCCESS;
828
647
const char *interface = "eth0";
829
struct ifreq network;
833
char *connect_to = NULL;
834
char tempdir[] = "/tmp/mandosXXXXXX";
835
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
836
const char *seckey = PATHDIR "/" SECKEY;
837
const char *pubkey = PATHDIR "/" PUBKEY;
839
mandos_context mc = { .simple_poll = NULL, .server = NULL,
840
.dh_bits = 1024, .priority = "SECURE256"
841
":!CTYPE-X.509:+CTYPE-OPENPGP" };
842
bool gnutls_initialized = false;
843
bool gpgme_initialized = false;
846
struct argp_option options[] = {
847
{ .name = "debug", .key = 128,
848
.doc = "Debug mode", .group = 3 },
849
{ .name = "connect", .key = 'c',
850
.arg = "ADDRESS:PORT",
851
.doc = "Connect directly to a specific Mandos server",
853
{ .name = "interface", .key = 'i',
855
.doc = "Interface that will be used to search for Mandos"
858
{ .name = "seckey", .key = 's',
860
.doc = "OpenPGP secret key file base name",
862
{ .name = "pubkey", .key = 'p',
864
.doc = "OpenPGP public key file base name",
866
{ .name = "dh-bits", .key = 129,
868
.doc = "Bit length of the prime number used in the"
869
" Diffie-Hellman key exchange",
871
{ .name = "priority", .key = 130,
873
.doc = "GnuTLS priority string for the TLS handshake",
878
error_t parse_opt(int key, char *arg,
879
struct argp_state *state) {
881
case 128: /* --debug */
884
case 'c': /* --connect */
887
case 'i': /* --interface */
890
case 's': /* --seckey */
893
case 'p': /* --pubkey */
896
case 129: /* --dh-bits */
897
ret = sscanf(arg, "%" SCNdMAX "%n", &tmpmax, &numchars);
898
if(ret < 1 or tmpmax != (typeof(mc.dh_bits))tmpmax
899
or arg[numchars] != '\0'){
900
fprintf(stderr, "Bad number of DH bits\n");
903
mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
905
case 130: /* --priority */
913
return ARGP_ERR_UNKNOWN;
918
struct argp argp = { .options = options, .parser = parse_opt,
920
.doc = "Mandos client -- Get and decrypt"
921
" passwords from a Mandos server" };
922
ret = argp_parse(&argp, argc, argv, 0, 0, NULL);
923
if(ret == ARGP_ERR_UNKNOWN){
924
fprintf(stderr, "Unknown error while parsing arguments\n");
925
exitcode = EXIT_FAILURE;
930
/* If the interface is down, bring it up */
932
sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
935
exitcode = EXIT_FAILURE;
938
strcpy(network.ifr_name, interface);
939
ret = ioctl(sd, SIOCGIFFLAGS, &network);
941
perror("ioctl SIOCGIFFLAGS");
942
exitcode = EXIT_FAILURE;
945
if((network.ifr_flags & IFF_UP) == 0){
946
network.ifr_flags |= IFF_UP;
947
ret = ioctl(sd, SIOCSIFFLAGS, &network);
949
perror("ioctl SIOCSIFFLAGS");
950
exitcode = EXIT_FAILURE;
954
ret = (int)TEMP_FAILURE_RETRY(close(sd));
973
ret = init_gnutls_global(&mc, pubkey, seckey);
975
fprintf(stderr, "init_gnutls_global failed\n");
976
exitcode = EXIT_FAILURE;
979
gnutls_initialized = true;
982
if(mkdtemp(tempdir) == NULL){
988
if(not init_gpgme(&mc, pubkey, seckey, tempdir)){
989
fprintf(stderr, "init_gpgme failed\n");
990
exitcode = EXIT_FAILURE;
993
gpgme_initialized = true;
996
if_index = (AvahiIfIndex) if_nametoindex(interface);
998
fprintf(stderr, "No such interface: \"%s\"\n", interface);
1002
if(connect_to != NULL){
1003
/* Connect directly, do not use Zeroconf */
1004
/* (Mainly meant for debugging) */
1005
char *address = strrchr(connect_to, ':');
1006
if(address == NULL){
1007
fprintf(stderr, "No colon in address\n");
1008
exitcode = EXIT_FAILURE;
1012
ret = sscanf(address+1, "%" SCNdMAX "%n", &tmpmax, &numchars);
1013
if(ret < 1 or tmpmax != (uint16_t)tmpmax
1014
or address[numchars+1] != '\0'){
1015
fprintf(stderr, "Bad port number\n");
1016
exitcode = EXIT_FAILURE;
1019
port = (uint16_t)tmpmax;
1021
address = connect_to;
1022
ret = start_mandos_communication(address, port, if_index, &mc);
1024
exitcode = EXIT_FAILURE;
1026
exitcode = EXIT_SUCCESS;
650
static struct option long_options[] = {
651
{"debug", no_argument, (int *)&debug, 1},
652
{"interface", required_argument, 0, 'i'},
653
{"certdir", required_argument, 0, 'd'},
654
{"certkey", required_argument, 0, 'c'},
655
{"certfile", required_argument, 0, 'k'},
658
int option_index = 0;
659
ret = getopt_long (argc, argv, "i:", long_options,
686
certfile = combinepath(certdir, certfile);
687
if (certfile == NULL){
691
certkey = combinepath(certdir, certkey);
692
if (certkey == NULL){
1032
697
avahi_set_log_function(empty_log);
1035
/* Initialize the pseudo-RNG for Avahi */
700
/* Initialize the psuedo-RNG */
1036
701
srand((unsigned int) time(NULL));
1038
/* Allocate main Avahi loop object */
1039
mc.simple_poll = avahi_simple_poll_new();
1040
if(mc.simple_poll == NULL) {
1041
fprintf(stderr, "Avahi: Failed to create simple poll"
1043
exitcode = EXIT_FAILURE;
1048
AvahiServerConfig config;
1049
/* Do not publish any local Zeroconf records */
1050
avahi_server_config_init(&config);
1051
config.publish_hinfo = 0;
1052
config.publish_addresses = 0;
1053
config.publish_workstation = 0;
1054
config.publish_domain = 0;
1056
/* Allocate a new server */
1057
mc.server = avahi_server_new(avahi_simple_poll_get
1058
(mc.simple_poll), &config, NULL,
1061
/* Free the Avahi configuration data */
1062
avahi_server_config_free(&config);
1065
/* Check if creating the Avahi server object succeeded */
1066
if(mc.server == NULL) {
1067
fprintf(stderr, "Failed to create Avahi server: %s\n",
703
/* Allocate main loop object */
704
if (!(simple_poll = avahi_simple_poll_new())) {
705
fprintf(stderr, "Failed to create simple poll object.\n");
710
/* Do not publish any local records */
711
avahi_server_config_init(&config);
712
config.publish_hinfo = 0;
713
config.publish_addresses = 0;
714
config.publish_workstation = 0;
715
config.publish_domain = 0;
717
/* Allocate a new server */
718
server = avahi_server_new(avahi_simple_poll_get(simple_poll),
719
&config, NULL, NULL, &error);
721
/* Free the configuration data */
722
avahi_server_config_free(&config);
724
/* Check if creating the server object succeeded */
726
fprintf(stderr, "Failed to create server: %s\n",
1068
727
avahi_strerror(error));
1069
exitcode = EXIT_FAILURE;
728
returncode = EXIT_FAILURE;
1073
/* Create the Avahi service browser */
1074
sb = avahi_s_service_browser_new(mc.server, if_index,
732
/* Create the service browser */
733
sb = avahi_s_service_browser_new(server,
735
if_nametoindex(interface),
1075
736
AVAHI_PROTO_INET6,
1076
737
"_mandos._tcp", NULL, 0,
1077
browse_callback, &mc);
738
browse_callback, server);
1079
740
fprintf(stderr, "Failed to create service browser: %s\n",
1080
avahi_strerror(avahi_server_errno(mc.server)));
1081
exitcode = EXIT_FAILURE;
741
avahi_strerror(avahi_server_errno(server)));
742
returncode = EXIT_FAILURE;
1085
746
/* Run the main loop */
1088
fprintf(stderr, "Starting Avahi loop search\n");
749
fprintf(stderr, "Starting avahi loop search\n");
1091
avahi_simple_poll_loop(mc.simple_poll);
752
avahi_simple_poll_loop(simple_poll);
1096
757
fprintf(stderr, "%s exiting\n", argv[0]);
1099
760
/* Cleanup things */
1101
762
avahi_s_service_browser_free(sb);
1103
if(mc.server != NULL)
1104
avahi_server_free(mc.server);
1106
if(mc.simple_poll != NULL)
1107
avahi_simple_poll_free(mc.simple_poll);
1109
if(gnutls_initialized){
1110
gnutls_certificate_free_credentials(mc.cred);
1111
gnutls_global_deinit();
1112
gnutls_dh_params_deinit(mc.dh_params);
1115
if(gpgme_initialized){
1116
gpgme_release(mc.ctx);
1119
/* Removes the temp directory used by GPGME */
1120
if(tempdir[0] != '\0'){
1122
struct dirent *direntry;
1123
d = opendir(tempdir);
1125
if(errno != ENOENT){
1130
direntry = readdir(d);
1131
if(direntry == NULL){
1134
/* Skip "." and ".." */
1135
if(direntry->d_name[0] == '.'
1136
and (direntry->d_name[1] == '\0'
1137
or (direntry->d_name[1] == '.'
1138
and direntry->d_name[2] == '\0'))){
1141
char *fullname = NULL;
1142
ret = asprintf(&fullname, "%s/%s", tempdir,
1148
ret = remove(fullname);
1150
fprintf(stderr, "remove(\"%s\"): %s\n", fullname,
1157
ret = rmdir(tempdir);
1158
if(ret == -1 and errno != ENOENT){
765
avahi_server_free(server);
768
avahi_simple_poll_free(simple_poll);