2
This file is part of avahi.
4
avahi is free software; you can redistribute it and/or modify it
5
under the terms of the GNU Lesser General Public License as
6
published by the Free Software Foundation; either version 2.1 of the
7
License, or (at your option) any later version.
9
avahi is distributed in the hope that it will be useful, but WITHOUT
10
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
12
Public License for more details.
14
You should have received a copy of the GNU Lesser General Public
15
License along with avahi; if not, write to the Free Software
16
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
1
/* -*- coding: utf-8 -*- */
3
* Mandos-client - get and decrypt data from a Mandos server
5
* This program is partly derived from an example program for an Avahi
6
* service browser, downloaded from
7
* <http://avahi.org/browser/examples/core-browse-services.c>. This
8
* includes the following functions: "resolve_callback",
9
* "browse_callback", and parts of "main".
12
* Copyright © 2008,2009 Teddy Hogeborn
13
* Copyright © 2008,2009 Björn Påhlsson
15
* This program is free software: you can redistribute it and/or
16
* modify it under the terms of the GNU General Public License as
17
* published by the Free Software Foundation, either version 3 of the
18
* License, or (at your option) any later version.
20
* This program is distributed in the hope that it will be useful, but
21
* WITHOUT ANY WARRANTY; without even the implied warranty of
22
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23
* General Public License for more details.
25
* You should have received a copy of the GNU General Public License
26
* along with this program. If not, see
27
* <http://www.gnu.org/licenses/>.
29
* Contact the authors at <mandos@fukt.bsnet.se>.
32
/* Needed by GPGME, specifically gpgme_data_seek() */
20
33
#define _LARGEFILE_SOURCE
21
34
#define _FILE_OFFSET_BITS 64
27
#include <net/if.h> /* if_nametoindex */
36
#define _GNU_SOURCE /* TEMP_FAILURE_RETRY(), asprintf() */
38
#include <stdio.h> /* fprintf(), stderr, fwrite(),
39
stdout, ferror(), sscanf */
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, 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, INET6_ADDRSTRLEN,
51
uid_t, gid_t, open(), opendir(), DIR */
52
#include <sys/stat.h> /* open() */
53
#include <sys/socket.h> /* socket(), struct sockaddr_in6,
54
struct in6_addr, inet_pton(),
56
#include <fcntl.h> /* open() */
57
#include <dirent.h> /* opendir(), struct dirent, readdir() */
58
#include <inttypes.h> /* PRIu16, SCNu16 */
59
#include <assert.h> /* assert() */
60
#include <errno.h> /* perror(), errno */
61
#include <time.h> /* time() */
62
#include <net/if.h> /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
63
SIOCSIFFLAGS, if_indextoname(),
64
if_nametoindex(), IF_NAMESIZE */
65
#include <netinet/in.h>
66
#include <unistd.h> /* close(), SEEK_SET, off_t, write(),
67
getuid(), getgid(), setuid(),
69
#include <arpa/inet.h> /* inet_pton(), htons */
70
#include <iso646.h> /* not, and, or */
71
#include <argp.h> /* struct argp_option, error_t, struct
72
argp_state, struct argp,
73
argp_parse(), ARGP_KEY_ARG,
74
ARGP_KEY_END, ARGP_ERR_UNKNOWN */
77
/* All Avahi types, constants and functions
29
80
#include <avahi-core/core.h>
30
81
#include <avahi-core/lookup.h>
31
82
#include <avahi-core/log.h>
33
84
#include <avahi-common/malloc.h>
34
85
#include <avahi-common/error.h>
37
#include <sys/types.h> /* socket(), setsockopt(), inet_pton() */
38
#include <sys/socket.h> /* socket(), setsockopt(), struct sockaddr_in6, struct in6_addr, inet_pton() */
39
#include <gnutls/gnutls.h> /* ALL GNUTLS STUFF */
40
#include <gnutls/openpgp.h> /* gnutls with openpgp stuff */
42
#include <unistd.h> /* close() */
43
#include <netinet/in.h>
44
#include <stdbool.h> /* true */
45
#include <string.h> /* memset */
46
#include <arpa/inet.h> /* inet_pton() */
47
#include <iso646.h> /* not */
50
#include <errno.h> /* perror() */
55
#define CERT_ROOT "/conf/conf.d/cryptkeyreq/"
57
#define CERTFILE CERT_ROOT "openpgp-client.txt"
58
#define KEYFILE CERT_ROOT "openpgp-client-key.txt"
88
#include <gnutls/gnutls.h> /* All GnuTLS types, constants and
91
init_gnutls_session(),
93
#include <gnutls/openpgp.h> /* gnutls_certificate_set_openpgp_key_file(),
94
GNUTLS_OPENPGP_FMT_BASE64 */
97
#include <gpgme.h> /* All GPGME types, constants and
100
GPGME_PROTOCOL_OpenPGP,
59
103
#define BUFFER_SIZE 256
105
#define PATHDIR "/conf/conf.d/mandos"
106
#define SECKEY "seckey.txt"
107
#define PUBKEY "pubkey.txt"
110
static const char mandos_protocol_version[] = "1";
111
const char *argp_program_version = "mandos-client " VERSION;
112
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
114
/* Used for passing in values through the Avahi callback functions */
65
gnutls_session_t session;
116
AvahiSimplePoll *simple_poll;
66
118
gnutls_certificate_credentials_t cred;
119
unsigned int dh_bits;
67
120
gnutls_dh_params_t dh_params;
71
ssize_t gpg_packet_decrypt (char *packet, size_t packet_size, char **new_packet, char *homedir){
72
gpgme_data_t dh_crypto, dh_plain;
121
const char *priority;
126
* Make room in "buffer" for at least BUFFER_SIZE additional bytes.
127
* "buffer_capacity" is how much is currently allocated,
128
* "buffer_length" is how much is already used.
130
size_t adjustbuffer(char **buffer, size_t buffer_length,
131
size_t buffer_capacity){
132
if (buffer_length + BUFFER_SIZE > buffer_capacity){
133
*buffer = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
137
buffer_capacity += BUFFER_SIZE;
139
return buffer_capacity;
145
static bool init_gpgme(mandos_context *mc, const char *seckey,
146
const char *pubkey, const char *tempdir){
76
size_t new_packet_capacity = 0;
77
size_t new_packet_length = 0;
78
149
gpgme_engine_info_t engine_info;
153
* Helper function to insert pub and seckey to the enigne keyring.
155
bool import_key(const char *filename){
157
gpgme_data_t pgp_data;
159
fd = (int)TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
165
rc = gpgme_data_new_from_fd(&pgp_data, fd);
166
if (rc != GPG_ERR_NO_ERROR){
167
fprintf(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
168
gpgme_strsource(rc), gpgme_strerror(rc));
172
rc = gpgme_op_import(mc->ctx, pgp_data);
173
if (rc != GPG_ERR_NO_ERROR){
174
fprintf(stderr, "bad gpgme_op_import: %s: %s\n",
175
gpgme_strsource(rc), gpgme_strerror(rc));
179
ret = (int)TEMP_FAILURE_RETRY(close(fd));
183
gpgme_data_release(pgp_data);
81
fprintf(stderr, "Attempting to decrypt password from gpg packet\n");
188
fprintf(stderr, "Initialize gpgme\n");
85
192
gpgme_check_version(NULL);
86
gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
193
rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
194
if (rc != GPG_ERR_NO_ERROR){
195
fprintf(stderr, "bad gpgme_engine_check_version: %s: %s\n",
196
gpgme_strsource(rc), gpgme_strerror(rc));
88
/* Set GPGME home directory */
200
/* Set GPGME home directory for the OpenPGP engine only */
89
201
rc = gpgme_get_engine_info (&engine_info);
90
202
if (rc != GPG_ERR_NO_ERROR){
91
203
fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
92
204
gpgme_strsource(rc), gpgme_strerror(rc));
95
207
while(engine_info != NULL){
96
208
if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
97
209
gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
98
engine_info->file_name, homedir);
210
engine_info->file_name, tempdir);
101
213
engine_info = engine_info->next;
103
215
if(engine_info == NULL){
104
fprintf(stderr, "Could not set home dir to %s\n", homedir);
108
/* Create new GPGME data buffer from packet buffer */
109
rc = gpgme_data_new_from_mem(&dh_crypto, packet, packet_size, 0);
216
fprintf(stderr, "Could not set GPGME home dir to %s\n", tempdir);
220
/* Create new GPGME "context" */
221
rc = gpgme_new(&(mc->ctx));
222
if (rc != GPG_ERR_NO_ERROR){
223
fprintf(stderr, "bad gpgme_new: %s: %s\n",
224
gpgme_strsource(rc), gpgme_strerror(rc));
228
if (not import_key(pubkey) or not import_key(seckey)){
236
* Decrypt OpenPGP data.
237
* Returns -1 on error
239
static ssize_t pgp_packet_decrypt (const mandos_context *mc,
240
const char *cryptotext,
243
gpgme_data_t dh_crypto, dh_plain;
246
size_t plaintext_capacity = 0;
247
ssize_t plaintext_length = 0;
250
fprintf(stderr, "Trying to decrypt OpenPGP data\n");
253
/* Create new GPGME data buffer from memory cryptotext */
254
rc = gpgme_data_new_from_mem(&dh_crypto, cryptotext, crypto_size,
110
256
if (rc != GPG_ERR_NO_ERROR){
111
257
fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
112
258
gpgme_strsource(rc), gpgme_strerror(rc));
118
264
if (rc != GPG_ERR_NO_ERROR){
119
265
fprintf(stderr, "bad gpgme_data_new: %s: %s\n",
120
266
gpgme_strsource(rc), gpgme_strerror(rc));
124
/* Create new GPGME "context" */
125
rc = gpgme_new(&ctx);
126
if (rc != GPG_ERR_NO_ERROR){
127
fprintf(stderr, "bad gpgme_new: %s: %s\n",
128
gpgme_strsource(rc), gpgme_strerror(rc));
132
/* Decrypt data from the FILE pointer to the plaintext data buffer */
133
rc = gpgme_op_decrypt(ctx, dh_crypto, dh_plain);
267
gpgme_data_release(dh_crypto);
271
/* Decrypt data from the cryptotext data buffer to the plaintext
273
rc = gpgme_op_decrypt(mc->ctx, dh_crypto, dh_plain);
134
274
if (rc != GPG_ERR_NO_ERROR){
135
275
fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
136
276
gpgme_strsource(rc), gpgme_strerror(rc));
277
plaintext_length = -1;
279
gpgme_decrypt_result_t result;
280
result = gpgme_op_decrypt_result(mc->ctx);
282
fprintf(stderr, "gpgme_op_decrypt_result failed\n");
284
fprintf(stderr, "Unsupported algorithm: %s\n",
285
result->unsupported_algorithm);
286
fprintf(stderr, "Wrong key usage: %u\n",
287
result->wrong_key_usage);
288
if(result->file_name != NULL){
289
fprintf(stderr, "File name: %s\n", result->file_name);
291
gpgme_recipient_t recipient;
292
recipient = result->recipients;
294
while(recipient != NULL){
295
fprintf(stderr, "Public key algorithm: %s\n",
296
gpgme_pubkey_algo_name(recipient->pubkey_algo));
297
fprintf(stderr, "Key ID: %s\n", recipient->keyid);
298
fprintf(stderr, "Secret key available: %s\n",
299
recipient->status == GPG_ERR_NO_SECKEY
301
recipient = recipient->next;
141
fprintf(stderr, "decryption of gpg packet succeeded\n");
145
gpgme_decrypt_result_t result;
146
result = gpgme_op_decrypt_result(ctx);
148
fprintf(stderr, "gpgme_op_decrypt_result failed\n");
150
fprintf(stderr, "Unsupported algorithm: %s\n", result->unsupported_algorithm);
151
fprintf(stderr, "Wrong key usage: %d\n", result->wrong_key_usage);
152
if(result->file_name != NULL){
153
fprintf(stderr, "File name: %s\n", result->file_name);
155
gpgme_recipient_t recipient;
156
recipient = result->recipients;
158
while(recipient != NULL){
159
fprintf(stderr, "Public key algorithm: %s\n",
160
gpgme_pubkey_algo_name(recipient->pubkey_algo));
161
fprintf(stderr, "Key ID: %s\n", recipient->keyid);
162
fprintf(stderr, "Secret key available: %s\n",
163
recipient->status == GPG_ERR_NO_SECKEY ? "No" : "Yes");
164
recipient = recipient->next;
170
/* Delete the GPGME FILE pointer cryptotext data buffer */
171
gpgme_data_release(dh_crypto);
310
fprintf(stderr, "Decryption of OpenPGP data succeeded\n");
173
313
/* Seek back to the beginning of the GPGME plaintext data buffer */
174
gpgme_data_seek(dh_plain, 0, SEEK_SET);
314
if (gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET) == -1){
315
perror("gpgme_data_seek");
316
plaintext_length = -1;
178
if (new_packet_length + BUFFER_SIZE > new_packet_capacity){
179
*new_packet = realloc(*new_packet, new_packet_capacity + BUFFER_SIZE);
180
if (*new_packet == NULL){
184
new_packet_capacity += BUFFER_SIZE;
322
plaintext_capacity = adjustbuffer(plaintext,
323
(size_t)plaintext_length,
325
if (plaintext_capacity == 0){
326
perror("adjustbuffer");
327
plaintext_length = -1;
187
ret = gpgme_data_read(dh_plain, *new_packet + new_packet_length, BUFFER_SIZE);
331
ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
188
333
/* Print the data, if any */
190
/* If password is empty, then a incorrect error will be printed */
194
339
perror("gpgme_data_read");
340
plaintext_length = -1;
197
new_packet_length += ret;
343
plaintext_length += ret;
201
fprintf(stderr, "decrypted password is: %s\n", *new_packet);
347
fprintf(stderr, "Decrypted password is: ");
348
for(ssize_t i = 0; i < plaintext_length; i++){
349
fprintf(stderr, "%02hhX ", (*plaintext)[i]);
351
fprintf(stderr, "\n");
204
/* Delete the GPGME plaintext data buffer */
356
/* Delete the GPGME cryptotext data buffer */
357
gpgme_data_release(dh_crypto);
359
/* Delete the GPGME plaintext data buffer */
205
360
gpgme_data_release(dh_plain);
206
return new_packet_length;
361
return plaintext_length;
209
364
static const char * safer_gnutls_strerror (int value) {
210
const char *ret = gnutls_strerror (value);
365
const char *ret = gnutls_strerror (value); /* Spurious warning */
212
367
ret = "(unknown)";
216
void debuggnutls(int level, const char* string){
217
fprintf(stderr, "%s", string);
371
/* GnuTLS log function callback */
372
static void debuggnutls(__attribute__((unused)) int level,
374
fprintf(stderr, "GnuTLS: %s", string);
220
int initgnutls(encrypted_session *es){
377
static int init_gnutls_global(mandos_context *mc,
378
const char *pubkeyfilename,
379
const char *seckeyfilename){
225
fprintf(stderr, "Initializing gnutls\n");
383
fprintf(stderr, "Initializing GnuTLS\n");
229
if ((ret = gnutls_global_init ())
230
!= GNUTLS_E_SUCCESS) {
231
fprintf (stderr, "global_init: %s\n", safer_gnutls_strerror(ret));
386
ret = gnutls_global_init();
387
if (ret != GNUTLS_E_SUCCESS) {
388
fprintf (stderr, "GnuTLS global_init: %s\n",
389
safer_gnutls_strerror(ret));
394
/* "Use a log level over 10 to enable all debugging options."
236
397
gnutls_global_set_log_level(11);
237
398
gnutls_global_set_log_function(debuggnutls);
241
/* openpgp credentials */
242
if ((ret = gnutls_certificate_allocate_credentials (&es->cred))
243
!= GNUTLS_E_SUCCESS) {
244
fprintf (stderr, "memory error: %s\n", safer_gnutls_strerror(ret));
401
/* OpenPGP credentials */
402
gnutls_certificate_allocate_credentials(&mc->cred);
403
if (ret != GNUTLS_E_SUCCESS){
404
fprintf (stderr, "GnuTLS memory error: %s\n", /* Spurious
406
safer_gnutls_strerror(ret));
407
gnutls_global_deinit ();
249
fprintf(stderr, "Attempting to use openpgp certificate %s"
250
" and keyfile %s as gnutls credentials\n", CERTFILE, KEYFILE);
412
fprintf(stderr, "Attempting to use OpenPGP public key %s and"
413
" secret key %s as GnuTLS credentials\n", pubkeyfilename,
253
417
ret = gnutls_certificate_set_openpgp_key_file
254
(es->cred, CERTFILE, KEYFILE, GNUTLS_OPENPGP_FMT_BASE64);
255
if (ret != GNUTLS_E_SUCCESS) {
257
(stderr, "Error[%d] while reading the OpenPGP key pair ('%s', '%s')\n",
258
ret, CERTFILE, KEYFILE);
259
fprintf(stdout, "The Error is: %s\n",
260
safer_gnutls_strerror(ret));
264
//Gnutls server initialization
265
if ((ret = gnutls_dh_params_init (&es->dh_params))
266
!= GNUTLS_E_SUCCESS) {
267
fprintf (stderr, "Error in dh parameter initialization: %s\n",
268
safer_gnutls_strerror(ret));
272
if ((ret = gnutls_dh_params_generate2 (es->dh_params, DH_BITS))
273
!= GNUTLS_E_SUCCESS) {
274
fprintf (stderr, "Error in prime generation: %s\n",
275
safer_gnutls_strerror(ret));
279
gnutls_certificate_set_dh_params (es->cred, es->dh_params);
281
// Gnutls session creation
282
if ((ret = gnutls_init (&es->session, GNUTLS_SERVER))
283
!= GNUTLS_E_SUCCESS){
284
fprintf(stderr, "Error in gnutls session initialization: %s\n",
285
safer_gnutls_strerror(ret));
288
if ((ret = gnutls_priority_set_direct (es->session, "NORMAL", &err))
289
!= GNUTLS_E_SUCCESS) {
290
fprintf(stderr, "Syntax error at: %s\n", err);
291
fprintf(stderr, "Gnutls error: %s\n",
292
safer_gnutls_strerror(ret));
296
if ((ret = gnutls_credentials_set
297
(es->session, GNUTLS_CRD_CERTIFICATE, es->cred))
298
!= GNUTLS_E_SUCCESS) {
299
fprintf(stderr, "Error setting a credentials set: %s\n",
300
safer_gnutls_strerror(ret));
418
(mc->cred, pubkeyfilename, seckeyfilename,
419
GNUTLS_OPENPGP_FMT_BASE64);
420
if (ret != GNUTLS_E_SUCCESS) {
422
"Error[%d] while reading the OpenPGP key pair ('%s',"
423
" '%s')\n", ret, pubkeyfilename, seckeyfilename);
424
fprintf(stderr, "The GnuTLS error is: %s\n",
425
safer_gnutls_strerror(ret));
429
/* GnuTLS server initialization */
430
ret = gnutls_dh_params_init(&mc->dh_params);
431
if (ret != GNUTLS_E_SUCCESS) {
432
fprintf (stderr, "Error in GnuTLS DH parameter initialization:"
433
" %s\n", safer_gnutls_strerror(ret));
436
ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
437
if (ret != GNUTLS_E_SUCCESS) {
438
fprintf (stderr, "Error in GnuTLS prime generation: %s\n",
439
safer_gnutls_strerror(ret));
443
gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
449
gnutls_certificate_free_credentials(mc->cred);
450
gnutls_global_deinit();
451
gnutls_dh_params_deinit(mc->dh_params);
455
static int init_gnutls_session(mandos_context *mc,
456
gnutls_session_t *session){
458
/* GnuTLS session creation */
459
ret = gnutls_init(session, GNUTLS_SERVER);
460
if (ret != GNUTLS_E_SUCCESS){
461
fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
462
safer_gnutls_strerror(ret));
467
ret = gnutls_priority_set_direct(*session, mc->priority, &err);
468
if (ret != GNUTLS_E_SUCCESS) {
469
fprintf(stderr, "Syntax error at: %s\n", err);
470
fprintf(stderr, "GnuTLS error: %s\n",
471
safer_gnutls_strerror(ret));
472
gnutls_deinit (*session);
477
ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
479
if (ret != GNUTLS_E_SUCCESS) {
480
fprintf(stderr, "Error setting GnuTLS credentials: %s\n",
481
safer_gnutls_strerror(ret));
482
gnutls_deinit (*session);
304
486
/* ignore client certificate if any. */
305
gnutls_certificate_server_set_request (es->session, GNUTLS_CERT_IGNORE);
487
gnutls_certificate_server_set_request (*session,
307
gnutls_dh_set_prime_bits (es->session, DH_BITS);
490
gnutls_dh_set_prime_bits (*session, mc->dh_bits);
312
void empty_log(AvahiLogLevel level, const char *txt){}
495
/* Avahi log function callback */
496
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
497
__attribute__((unused)) const char *txt){}
314
int start_mandos_communcation(char *ip, uint16_t port){
499
/* Called when a Mandos server is found */
500
static int start_mandos_communication(const char *ip, uint16_t port,
501
AvahiIfIndex if_index,
316
struct sockaddr_in6 to;
317
struct in6_addr ip_addr;
318
encrypted_session es;
505
union { struct sockaddr in; struct sockaddr_in6 in6; } to;
319
506
char *buffer = NULL;
320
507
char *decrypted_buffer;
321
508
size_t buffer_length = 0;
322
509
size_t buffer_capacity = 0;
323
510
ssize_t decrypted_buffer_size;
325
const char interface[] = "eth0";
513
char interface[IF_NAMESIZE];
514
gnutls_session_t session;
516
ret = init_gnutls_session (mc, &session);
328
fprintf(stderr, "Setting up a tcp connection to %s\n", ip);
522
fprintf(stderr, "Setting up a tcp connection to %s, port %" PRIu16
331
526
tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
333
528
perror("socket");
533
if(if_indextoname((unsigned int)if_index, interface) == NULL){
534
perror("if_indextoname");
338
537
fprintf(stderr, "Binding to interface %s\n", interface);
341
ret = setsockopt(tcp_sd, SOL_SOCKET, SO_BINDTODEVICE, interface, 5);
343
perror("setsockopt bindtodevice");
347
memset(&to,0,sizeof(to));
348
to.sin6_family = AF_INET6;
349
ret = inet_pton(AF_INET6, ip, &ip_addr);
540
memset(&to, 0, sizeof(to));
541
to.in6.sin6_family = AF_INET6;
542
/* It would be nice to have a way to detect if we were passed an
543
IPv4 address here. Now we assume an IPv6 address. */
544
ret = inet_pton(AF_INET6, ip, &to.in6.sin6_addr);
351
546
perror("inet_pton");
355
550
fprintf(stderr, "Bad address: %s\n", ip);
358
to.sin6_port = htons(port);
359
to.sin6_scope_id = if_nametoindex(interface);
553
to.in6.sin6_port = htons(port); /* Spurious warning */
555
to.in6.sin6_scope_id = (uint32_t)if_index;
362
fprintf(stderr, "Connection to: %s\n", ip);
558
fprintf(stderr, "Connection to: %s, port %" PRIu16 "\n", ip,
560
char addrstr[INET6_ADDRSTRLEN] = "";
561
if(inet_ntop(to.in6.sin6_family, &(to.in6.sin6_addr), addrstr,
562
sizeof(addrstr)) == NULL){
565
if(strcmp(addrstr, ip) != 0){
566
fprintf(stderr, "Canonical address form: %s\n", addrstr);
365
ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
571
ret = connect(tcp_sd, &to.in, sizeof(to));
367
573
perror("connect");
371
ret = initgnutls (&es);
577
const char *out = mandos_protocol_version;
580
size_t out_size = strlen(out);
581
ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
582
out_size - written));
588
written += (size_t)ret;
589
if(written < out_size){
592
if (out == mandos_protocol_version){
378
gnutls_transport_set_ptr (es.session, (gnutls_transport_ptr_t) tcp_sd);
381
fprintf(stderr, "Establishing tls session with %s\n", ip);
602
fprintf(stderr, "Establishing TLS session with %s\n", ip);
385
ret = gnutls_handshake (es.session);
605
gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t) tcp_sd);
608
ret = gnutls_handshake (session);
609
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
387
611
if (ret != GNUTLS_E_SUCCESS){
388
fprintf(stderr, "\n*** Handshake failed ***\n");
613
fprintf(stderr, "*** GnuTLS Handshake failed ***\n");
394
//Retrieve gpg packet that contains the wanted password
620
/* Read OpenPGP packet that contains the wanted password */
397
fprintf(stderr, "Retrieving pgp encrypted password from %s\n", ip);
623
fprintf(stderr, "Retrieving pgp encrypted password from %s\n",
401
if (buffer_length + BUFFER_SIZE > buffer_capacity){
402
buffer = realloc(buffer, buffer_capacity + BUFFER_SIZE);
407
buffer_capacity += BUFFER_SIZE;
628
buffer_capacity = adjustbuffer(&buffer, buffer_length,
630
if (buffer_capacity == 0){
631
perror("adjustbuffer");
410
ret = gnutls_record_recv
411
(es.session, buffer+buffer_length, BUFFER_SIZE);
636
sret = gnutls_record_recv(session, buffer+buffer_length,
417
643
case GNUTLS_E_INTERRUPTED:
418
644
case GNUTLS_E_AGAIN:
420
646
case GNUTLS_E_REHANDSHAKE:
421
ret = gnutls_handshake (es.session);
648
ret = gnutls_handshake (session);
649
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
423
fprintf(stderr, "\n*** Handshake failed ***\n");
651
fprintf(stderr, "*** GnuTLS Re-handshake failed ***\n");
424
652
gnutls_perror (ret);
430
fprintf(stderr, "Unknown error while reading data from encrypted session with mandos server\n");
658
fprintf(stderr, "Unknown error while reading data from"
659
" encrypted session with Mandos server\n");
432
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
661
gnutls_bye (session, GNUTLS_SHUT_RDWR);
436
buffer_length += ret;
665
buffer_length += (size_t) sret;
670
fprintf(stderr, "Closing TLS session\n");
673
gnutls_bye (session, GNUTLS_SHUT_RDWR);
440
675
if (buffer_length > 0){
441
if ((decrypted_buffer_size = gpg_packet_decrypt(buffer, buffer_length, &decrypted_buffer, CERT_ROOT)) >= 0){
442
fwrite (decrypted_buffer, 1, decrypted_buffer_size, stdout);
676
decrypted_buffer_size = pgp_packet_decrypt(mc, buffer,
679
if (decrypted_buffer_size >= 0){
681
while(written < (size_t) decrypted_buffer_size){
682
ret = (int)fwrite (decrypted_buffer + written, 1,
683
(size_t)decrypted_buffer_size - written,
685
if(ret == 0 and ferror(stdout)){
687
fprintf(stderr, "Error writing encrypted data: %s\n",
693
written += (size_t)ret;
443
695
free(decrypted_buffer);
452
fprintf(stderr, "Closing tls session\n");
703
/* Shutdown procedure */
456
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
459
gnutls_deinit (es.session);
460
gnutls_certificate_free_credentials (es.cred);
461
gnutls_global_deinit ();
707
ret = (int)TEMP_FAILURE_RETRY(close(tcp_sd));
711
gnutls_deinit (session);
465
static AvahiSimplePoll *simple_poll = NULL;
466
static AvahiServer *server = NULL;
468
static void resolve_callback(
469
AvahiSServiceResolver *r,
470
AVAHI_GCC_UNUSED AvahiIfIndex interface,
471
AVAHI_GCC_UNUSED AvahiProtocol protocol,
472
AvahiResolverEvent event,
476
const char *host_name,
477
const AvahiAddress *address,
479
AvahiStringList *txt,
480
AvahiLookupResultFlags flags,
481
AVAHI_GCC_UNUSED void* userdata) {
485
/* Called whenever a service has been resolved successfully or timed out */
488
case AVAHI_RESOLVER_FAILURE:
489
fprintf(stderr, "(Resolver) Failed to resolve service '%s' of type '%s' in domain '%s': %s\n", name, type, domain, avahi_strerror(avahi_server_errno(server)));
492
case AVAHI_RESOLVER_FOUND: {
493
char ip[AVAHI_ADDRESS_STR_MAX];
494
avahi_address_snprint(ip, sizeof(ip), address);
496
fprintf(stderr, "Mandos server found at %s on port %d\n", ip, port);
498
int ret = start_mandos_communcation(ip, port);
506
avahi_s_service_resolver_free(r);
509
static void browse_callback(
510
AvahiSServiceBrowser *b,
511
AvahiIfIndex interface,
512
AvahiProtocol protocol,
513
AvahiBrowserEvent event,
517
AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
520
AvahiServer *s = userdata;
523
/* Called whenever a new services becomes available on the LAN or is removed from the LAN */
527
case AVAHI_BROWSER_FAILURE:
529
fprintf(stderr, "(Browser) %s\n", avahi_strerror(avahi_server_errno(server)));
530
avahi_simple_poll_quit(simple_poll);
533
case AVAHI_BROWSER_NEW:
534
/* We ignore the returned resolver object. In the callback
535
function we free it. If the server is terminated before
536
the callback function is called the server will free
537
the resolver for us. */
539
if (!(avahi_s_service_resolver_new(s, interface, protocol, name, type, domain, AVAHI_PROTO_INET6, 0, resolve_callback, s)))
540
fprintf(stderr, "Failed to resolve service '%s': %s\n", name, avahi_strerror(avahi_server_errno(s)));
544
case AVAHI_BROWSER_REMOVE:
547
case AVAHI_BROWSER_ALL_FOR_NOW:
548
case AVAHI_BROWSER_CACHE_EXHAUSTED:
553
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
554
AvahiServerConfig config;
715
static void resolve_callback(AvahiSServiceResolver *r,
716
AvahiIfIndex interface,
717
AVAHI_GCC_UNUSED AvahiProtocol protocol,
718
AvahiResolverEvent event,
722
const char *host_name,
723
const AvahiAddress *address,
725
AVAHI_GCC_UNUSED AvahiStringList *txt,
726
AVAHI_GCC_UNUSED AvahiLookupResultFlags
729
mandos_context *mc = userdata;
732
/* Called whenever a service has been resolved successfully or
737
case AVAHI_RESOLVER_FAILURE:
738
fprintf(stderr, "(Avahi Resolver) Failed to resolve service '%s'"
739
" of type '%s' in domain '%s': %s\n", name, type, domain,
740
avahi_strerror(avahi_server_errno(mc->server)));
743
case AVAHI_RESOLVER_FOUND:
745
char ip[AVAHI_ADDRESS_STR_MAX];
746
avahi_address_snprint(ip, sizeof(ip), address);
748
fprintf(stderr, "Mandos server \"%s\" found on %s (%s, %"
749
PRIu16 ") on port %d\n", name, host_name, ip,
752
int ret = start_mandos_communication(ip, port, interface, mc);
754
avahi_simple_poll_quit(mc->simple_poll);
758
avahi_s_service_resolver_free(r);
761
static void browse_callback( AvahiSServiceBrowser *b,
762
AvahiIfIndex interface,
763
AvahiProtocol protocol,
764
AvahiBrowserEvent event,
768
AVAHI_GCC_UNUSED AvahiLookupResultFlags
771
mandos_context *mc = userdata;
774
/* Called whenever a new services becomes available on the LAN or
775
is removed from the LAN */
779
case AVAHI_BROWSER_FAILURE:
781
fprintf(stderr, "(Avahi browser) %s\n",
782
avahi_strerror(avahi_server_errno(mc->server)));
783
avahi_simple_poll_quit(mc->simple_poll);
786
case AVAHI_BROWSER_NEW:
787
/* We ignore the returned Avahi resolver object. In the callback
788
function we free it. If the Avahi server is terminated before
789
the callback function is called the Avahi server will free the
792
if (!(avahi_s_service_resolver_new(mc->server, interface,
793
protocol, name, type, domain,
794
AVAHI_PROTO_INET6, 0,
795
resolve_callback, mc)))
796
fprintf(stderr, "Avahi: Failed to resolve service '%s': %s\n",
797
name, avahi_strerror(avahi_server_errno(mc->server)));
800
case AVAHI_BROWSER_REMOVE:
803
case AVAHI_BROWSER_ALL_FOR_NOW:
804
case AVAHI_BROWSER_CACHE_EXHAUSTED:
806
fprintf(stderr, "No Mandos server found, still searching...\n");
812
int main(int argc, char *argv[]){
555
813
AvahiSServiceBrowser *sb = NULL;
556
const char db[] = "--debug";
559
int returncode = EXIT_SUCCESS;
560
char *basename = rindex(argv[0], '/');
561
if(basename == NULL){
567
char *program_name = malloc(strlen(basename) + sizeof(db));
569
if (program_name == NULL){
574
program_name[0] = '\0';
576
for (int i = 1; i < argc; i++){
577
if (not strncmp(argv[i], db, 5)){
578
strcat(strcat(strcat(program_name, db ), "="), basename);
579
if(not strcmp(argv[i], db) or not strcmp(argv[i], program_name)){
816
int exitcode = EXIT_SUCCESS;
817
const char *interface = "eth0";
818
struct ifreq network;
822
char *connect_to = NULL;
823
char tempdir[] = "/tmp/mandosXXXXXX";
824
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
825
const char *seckey = PATHDIR "/" SECKEY;
826
const char *pubkey = PATHDIR "/" PUBKEY;
828
mandos_context mc = { .simple_poll = NULL, .server = NULL,
829
.dh_bits = 1024, .priority = "SECURE256"
830
":!CTYPE-X.509:+CTYPE-OPENPGP" };
831
bool gnutls_initalized = false;
832
bool gpgme_initalized = false;
835
struct argp_option options[] = {
836
{ .name = "debug", .key = 128,
837
.doc = "Debug mode", .group = 3 },
838
{ .name = "connect", .key = 'c',
839
.arg = "ADDRESS:PORT",
840
.doc = "Connect directly to a specific Mandos server",
842
{ .name = "interface", .key = 'i',
844
.doc = "Interface that will be used to search for Mandos"
847
{ .name = "seckey", .key = 's',
849
.doc = "OpenPGP secret key file base name",
851
{ .name = "pubkey", .key = 'p',
853
.doc = "OpenPGP public key file base name",
855
{ .name = "dh-bits", .key = 129,
857
.doc = "Bit length of the prime number used in the"
858
" Diffie-Hellman key exchange",
860
{ .name = "priority", .key = 130,
862
.doc = "GnuTLS priority string for the TLS handshake",
867
error_t parse_opt (int key, char *arg,
868
struct argp_state *state) {
870
case 128: /* --debug */
873
case 'c': /* --connect */
876
case 'i': /* --interface */
879
case 's': /* --seckey */
882
case 'p': /* --pubkey */
885
case 129: /* --dh-bits */
886
ret = sscanf(arg, "%u", &mc.dh_bits);
887
if(ret == 0 or mc.dh_bits == 0){
888
fprintf(stderr, "Bad number of DH bits\n");
892
case 130: /* --priority */
900
return ARGP_ERR_UNKNOWN;
905
struct argp argp = { .options = options, .parser = parse_opt,
907
.doc = "Mandos client -- Get and decrypt"
908
" passwords from a Mandos server" };
909
ret = argp_parse (&argp, argc, argv, 0, 0, NULL);
910
if (ret == ARGP_ERR_UNKNOWN){
911
fprintf(stderr, "Unknown error while parsing arguments\n");
912
exitcode = EXIT_FAILURE;
917
/* If the interface is down, bring it up */
919
sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
922
exitcode = EXIT_FAILURE;
925
strcpy(network.ifr_name, interface);
926
ret = ioctl(sd, SIOCGIFFLAGS, &network);
928
perror("ioctl SIOCGIFFLAGS");
929
exitcode = EXIT_FAILURE;
932
if((network.ifr_flags & IFF_UP) == 0){
933
network.ifr_flags |= IFF_UP;
934
ret = ioctl(sd, SIOCSIFFLAGS, &network);
936
perror("ioctl SIOCSIFFLAGS");
937
exitcode = EXIT_FAILURE;
941
ret = (int)TEMP_FAILURE_RETRY(close(sd));
960
ret = init_gnutls_global(&mc, pubkey, seckey);
962
fprintf(stderr, "init_gnutls_global failed\n");
963
exitcode = EXIT_FAILURE;
966
gnutls_initalized = true;
969
if(mkdtemp(tempdir) == NULL){
975
if(not init_gpgme(&mc, pubkey, seckey, tempdir)){
976
fprintf(stderr, "gpgme_initalized failed\n");
977
exitcode = EXIT_FAILURE;
980
gpgme_initalized = true;
983
if_index = (AvahiIfIndex) if_nametoindex(interface);
985
fprintf(stderr, "No such interface: \"%s\"\n", interface);
989
if(connect_to != NULL){
990
/* Connect directly, do not use Zeroconf */
991
/* (Mainly meant for debugging) */
992
char *address = strrchr(connect_to, ':');
994
fprintf(stderr, "No colon in address\n");
995
exitcode = EXIT_FAILURE;
999
ret = sscanf(address+1, "%" SCNu16, &port);
1000
if(ret == 0 or port == 0){
1001
fprintf(stderr, "Bad port number\n");
1002
exitcode = EXIT_FAILURE;
1006
address = connect_to;
1007
ret = start_mandos_communication(address, port, if_index, &mc);
1009
exitcode = EXIT_FAILURE;
1011
exitcode = EXIT_SUCCESS;
587
1017
avahi_set_log_function(empty_log);
590
/* Initialize the psuedo-RNG */
593
/* Allocate main loop object */
594
if (!(simple_poll = avahi_simple_poll_new())) {
595
fprintf(stderr, "Failed to create simple poll object.\n");
600
/* Do not publish any local records */
601
avahi_server_config_init(&config);
602
config.publish_hinfo = 0;
603
config.publish_addresses = 0;
604
config.publish_workstation = 0;
605
config.publish_domain = 0;
607
/* Allocate a new server */
608
server = avahi_server_new(avahi_simple_poll_get(simple_poll), &config, NULL, NULL, &error);
610
/* Free the configuration data */
611
avahi_server_config_free(&config);
613
/* Check if creating the server object succeeded */
615
fprintf(stderr, "Failed to create server: %s\n", avahi_strerror(error));
616
returncode = EXIT_FAILURE;
620
/* Create the service browser */
621
if (!(sb = avahi_s_service_browser_new(server, if_nametoindex("eth0"), AVAHI_PROTO_INET6, "_mandos._tcp", NULL, 0, browse_callback, server))) {
622
fprintf(stderr, "Failed to create service browser: %s\n", avahi_strerror(avahi_server_errno(server)));
623
returncode = EXIT_FAILURE;
1020
/* Initialize the pseudo-RNG for Avahi */
1021
srand((unsigned int) time(NULL));
1023
/* Allocate main Avahi loop object */
1024
mc.simple_poll = avahi_simple_poll_new();
1025
if (mc.simple_poll == NULL) {
1026
fprintf(stderr, "Avahi: Failed to create simple poll"
1028
exitcode = EXIT_FAILURE;
1033
AvahiServerConfig config;
1034
/* Do not publish any local Zeroconf records */
1035
avahi_server_config_init(&config);
1036
config.publish_hinfo = 0;
1037
config.publish_addresses = 0;
1038
config.publish_workstation = 0;
1039
config.publish_domain = 0;
1041
/* Allocate a new server */
1042
mc.server = avahi_server_new(avahi_simple_poll_get
1043
(mc.simple_poll), &config, NULL,
1046
/* Free the Avahi configuration data */
1047
avahi_server_config_free(&config);
1050
/* Check if creating the Avahi server object succeeded */
1051
if (mc.server == NULL) {
1052
fprintf(stderr, "Failed to create Avahi server: %s\n",
1053
avahi_strerror(error));
1054
exitcode = EXIT_FAILURE;
1058
/* Create the Avahi service browser */
1059
sb = avahi_s_service_browser_new(mc.server, if_index,
1061
"_mandos._tcp", NULL, 0,
1062
browse_callback, &mc);
1064
fprintf(stderr, "Failed to create service browser: %s\n",
1065
avahi_strerror(avahi_server_errno(mc.server)));
1066
exitcode = EXIT_FAILURE;
627
1070
/* Run the main loop */
630
fprintf(stderr, "Starting avahi loop search\n");
1073
fprintf(stderr, "Starting Avahi loop search\n");
633
avahi_simple_poll_loop(simple_poll);
1076
avahi_simple_poll_loop(mc.simple_poll);
638
1081
fprintf(stderr, "%s exiting\n", argv[0]);
641
1084
/* Cleanup things */
643
1086
avahi_s_service_browser_free(sb);
646
avahi_server_free(server);
649
avahi_simple_poll_free(simple_poll);
1088
if (mc.server != NULL)
1089
avahi_server_free(mc.server);
1091
if (mc.simple_poll != NULL)
1092
avahi_simple_poll_free(mc.simple_poll);
1094
if (gnutls_initalized){
1095
gnutls_certificate_free_credentials(mc.cred);
1096
gnutls_global_deinit ();
1097
gnutls_dh_params_deinit(mc.dh_params);
1100
if(gpgme_initalized){
1101
gpgme_release(mc.ctx);
1104
/* Removes the temp directory used by GPGME */
1105
if(tempdir[0] != '\0'){
1107
struct dirent *direntry;
1108
d = opendir(tempdir);
1110
if(errno != ENOENT){
1115
direntry = readdir(d);
1116
if(direntry == NULL){
1119
if (direntry->d_type == DT_REG){
1120
char *fullname = NULL;
1121
ret = asprintf(&fullname, "%s/%s", tempdir,
1127
ret = unlink(fullname);
1129
fprintf(stderr, "unlink(\"%s\"): %s",
1130
fullname, strerror(errno));
1137
ret = rmdir(tempdir);
1138
if(ret == -1 and errno != ENOENT){