1
/* -*- coding: utf-8 -*- */
3
* Mandos client - get and decrypt data from a Mandos server
4
* This file demonstrates how to use Avahi's core API, this is
5
* the embeddable mDNS stack for embedded applications.
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 Teddy Hogeborn & Björn Påhlsson
14
* This program is free software: you can redistribute it and/or
15
* modify it under the terms of the GNU General Public License as
16
* published by the Free Software Foundation, either version 3 of the
17
* License, or (at your option) any later version.
19
* This program is distributed in the hope that it will be useful, but
20
* WITHOUT ANY WARRANTY; without even the implied warranty of
21
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22
* General Public License for more details.
24
* You should have received a copy of the GNU General Public License
25
* along with this program. If not, see
26
* <http://www.gnu.org/licenses/>.
28
* Contact the authors at <mandos@fukt.bsnet.se>.
7
* End user applications should *not* use this API and should use
8
* the D-Bus or C APIs, please see
9
* client-browse-services.c and glib-integration.c
11
* I repeat, you probably do *not* want to use this example.
31
/* Needed by GPGME, specifically gpgme_data_seek() */
15
This file is part of avahi.
17
avahi is free software; you can redistribute it and/or modify it
18
under the terms of the GNU Lesser General Public License as
19
published by the Free Software Foundation; either version 2.1 of the
20
License, or (at your option) any later version.
22
avahi is distributed in the hope that it will be useful, but WITHOUT
23
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
24
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
25
Public License for more details.
27
You should have received a copy of the GNU Lesser General Public
28
License along with avahi; if not, write to the Free Software
29
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
32
33
#define _LARGEFILE_SOURCE
33
34
#define _FILE_OFFSET_BITS 64
35
#define _GNU_SOURCE /* TEMP_FAILURE_RETRY(), asprintf() */
37
#include <stdio.h> /* fprintf(), stderr, fwrite(),
39
#include <stdint.h> /* uint16_t, uint32_t */
40
#include <stddef.h> /* NULL, size_t, ssize_t */
41
#include <stdlib.h> /* free(), EXIT_SUCCESS, EXIT_FAILURE,
43
#include <stdbool.h> /* bool, true */
44
#include <string.h> /* memset(), strcmp(), strlen(),
45
strerror(), asprintf(), strcpy() */
46
#include <sys/ioctl.h> /* ioctl */
47
#include <sys/types.h> /* socket(), inet_pton(), sockaddr,
48
sockaddr_in6, PF_INET6,
49
SOCK_STREAM, INET6_ADDRSTRLEN,
50
uid_t, gid_t, open(), opendir(), DIR */
51
#include <sys/stat.h> /* open() */
52
#include <sys/socket.h> /* socket(), struct sockaddr_in6,
53
struct in6_addr, inet_pton(),
55
#include <fcntl.h> /* open() */
56
#include <dirent.h> /* opendir(), struct dirent, readdir() */
57
#include <inttypes.h> /* PRIu16 */
58
#include <assert.h> /* assert() */
59
#include <errno.h> /* perror(), errno */
60
#include <time.h> /* time() */
61
#include <net/if.h> /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
62
SIOCSIFFLAGS, if_indextoname(),
63
if_nametoindex(), IF_NAMESIZE */
64
#include <netinet/in.h>
65
#include <unistd.h> /* close(), SEEK_SET, off_t, write(),
66
getuid(), getgid(), setuid(),
68
#include <arpa/inet.h> /* inet_pton(), htons */
69
#include <iso646.h> /* not, and */
70
#include <argp.h> /* struct argp_option, error_t, struct
71
argp_state, struct argp,
72
argp_parse(), ARGP_KEY_ARG,
73
ARGP_KEY_END, ARGP_ERR_UNKNOWN */
76
/* All Avahi types, constants and functions
40
#include <net/if.h> /* if_nametoindex */
79
42
#include <avahi-core/core.h>
80
43
#include <avahi-core/lookup.h>
81
44
#include <avahi-core/log.h>
83
46
#include <avahi-common/malloc.h>
84
47
#include <avahi-common/error.h>
87
#include <gnutls/gnutls.h> /* All GnuTLS types, constants and
90
init_gnutls_session(),
92
#include <gnutls/openpgp.h> /* gnutls_certificate_set_openpgp_key_file(),
93
GNUTLS_OPENPGP_FMT_BASE64 */
96
#include <gpgme.h> /* All GPGME types, constants and
99
GPGME_PROTOCOL_OpenPGP,
50
#include <sys/types.h> /* socket(), setsockopt(), inet_pton() */
51
#include <sys/socket.h> /* socket(), setsockopt(), struct sockaddr_in6, struct in6_addr, inet_pton() */
52
#include <gnutls/gnutls.h> /* ALL GNUTLS STUFF */
53
#include <gnutls/openpgp.h> /* gnutls with openpgp stuff */
55
#include <unistd.h> /* close() */
56
#include <netinet/in.h>
57
#include <stdbool.h> /* true */
58
#include <string.h> /* memset */
59
#include <arpa/inet.h> /* inet_pton() */
60
#include <iso646.h> /* not */
63
#include <errno.h> /* perror() */
68
#define CERT_ROOT "/conf/conf.d/cryptkeyreq/"
70
#define CERTFILE CERT_ROOT "openpgp-client.txt"
71
#define KEYFILE CERT_ROOT "openpgp-client-key.txt"
102
72
#define BUFFER_SIZE 256
104
#define PATHDIR "/conf/conf.d/mandos"
105
#define SECKEY "seckey.txt"
106
#define PUBKEY "pubkey.txt"
109
static const char mandos_protocol_version[] = "1";
110
const char *argp_program_version = "mandos-client " VERSION;
111
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
113
/* Used for passing in values through the Avahi callback functions */
115
AvahiSimplePoll *simple_poll;
76
gnutls_session_t session;
117
77
gnutls_certificate_credentials_t cred;
118
unsigned int dh_bits;
119
78
gnutls_dh_params_t dh_params;
120
const char *priority;
82
ssize_t gpg_packet_decrypt (char *packet, size_t packet_size, char **new_packet, char *homedir){
83
gpgme_data_t dh_crypto, dh_plain;
125
* Make room in "buffer" for at least BUFFER_SIZE additional bytes.
126
* "buffer_capacity" is how much is currently allocated,
127
* "buffer_length" is how much is already used.
129
size_t adjustbuffer(char **buffer, size_t buffer_length,
130
size_t buffer_capacity){
131
if (buffer_length + BUFFER_SIZE > buffer_capacity){
132
*buffer = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
136
buffer_capacity += BUFFER_SIZE;
138
return buffer_capacity;
144
static bool init_gpgme(mandos_context *mc, const char *seckey,
145
const char *pubkey, const char *tempdir){
87
size_t new_packet_capacity = 0;
88
size_t new_packet_length = 0;
148
89
gpgme_engine_info_t engine_info;
152
* Helper function to insert pub and seckey to the enigne keyring.
154
bool import_key(const char *filename){
156
gpgme_data_t pgp_data;
158
fd = TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
164
rc = gpgme_data_new_from_fd(&pgp_data, fd);
165
if (rc != GPG_ERR_NO_ERROR){
166
fprintf(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
167
gpgme_strsource(rc), gpgme_strerror(rc));
171
rc = gpgme_op_import(mc->ctx, pgp_data);
172
if (rc != GPG_ERR_NO_ERROR){
173
fprintf(stderr, "bad gpgme_op_import: %s: %s\n",
174
gpgme_strsource(rc), gpgme_strerror(rc));
178
ret = TEMP_FAILURE_RETRY(close(fd));
182
gpgme_data_release(pgp_data);
187
fprintf(stderr, "Initialize gpgme\n");
191
92
gpgme_check_version(NULL);
192
rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
193
if (rc != GPG_ERR_NO_ERROR){
194
fprintf(stderr, "bad gpgme_engine_check_version: %s: %s\n",
195
gpgme_strsource(rc), gpgme_strerror(rc));
93
gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
199
/* Set GPGME home directory for the OpenPGP engine only */
95
/* Set GPGME home directory */
200
96
rc = gpgme_get_engine_info (&engine_info);
201
97
if (rc != GPG_ERR_NO_ERROR){
202
98
fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
203
99
gpgme_strsource(rc), gpgme_strerror(rc));
206
102
while(engine_info != NULL){
207
103
if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
208
104
gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
209
engine_info->file_name, tempdir);
105
engine_info->file_name, homedir);
212
108
engine_info = engine_info->next;
214
110
if(engine_info == NULL){
215
fprintf(stderr, "Could not set GPGME home dir to %s\n", tempdir);
219
/* Create new GPGME "context" */
220
rc = gpgme_new(&(mc->ctx));
221
if (rc != GPG_ERR_NO_ERROR){
222
fprintf(stderr, "bad gpgme_new: %s: %s\n",
223
gpgme_strsource(rc), gpgme_strerror(rc));
227
if (not import_key(pubkey) or not import_key(seckey)){
235
* Decrypt OpenPGP data.
236
* Returns -1 on error
238
static ssize_t pgp_packet_decrypt (const mandos_context *mc,
239
const char *cryptotext,
242
gpgme_data_t dh_crypto, dh_plain;
245
size_t plaintext_capacity = 0;
246
ssize_t plaintext_length = 0;
249
fprintf(stderr, "Trying to decrypt OpenPGP data\n");
252
/* Create new GPGME data buffer from memory cryptotext */
253
rc = gpgme_data_new_from_mem(&dh_crypto, cryptotext, crypto_size,
111
fprintf(stderr, "Could not set home dir to %s\n", homedir);
115
/* Create new GPGME data buffer from packet buffer */
116
rc = gpgme_data_new_from_mem(&dh_crypto, packet, packet_size, 0);
255
117
if (rc != GPG_ERR_NO_ERROR){
256
118
fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
257
119
gpgme_strsource(rc), gpgme_strerror(rc));
263
125
if (rc != GPG_ERR_NO_ERROR){
264
126
fprintf(stderr, "bad gpgme_data_new: %s: %s\n",
265
127
gpgme_strsource(rc), gpgme_strerror(rc));
266
gpgme_data_release(dh_crypto);
270
/* Decrypt data from the cryptotext data buffer to the plaintext
272
rc = gpgme_op_decrypt(mc->ctx, dh_crypto, dh_plain);
131
/* Create new GPGME "context" */
132
rc = gpgme_new(&ctx);
133
if (rc != GPG_ERR_NO_ERROR){
134
fprintf(stderr, "bad gpgme_new: %s: %s\n",
135
gpgme_strsource(rc), gpgme_strerror(rc));
139
/* Decrypt data from the FILE pointer to the plaintext data buffer */
140
rc = gpgme_op_decrypt(ctx, dh_crypto, dh_plain);
273
141
if (rc != GPG_ERR_NO_ERROR){
274
142
fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
275
143
gpgme_strsource(rc), gpgme_strerror(rc));
276
plaintext_length = -1;
278
gpgme_decrypt_result_t result;
279
result = gpgme_op_decrypt_result(mc->ctx);
281
fprintf(stderr, "gpgme_op_decrypt_result failed\n");
283
fprintf(stderr, "Unsupported algorithm: %s\n",
284
result->unsupported_algorithm);
285
fprintf(stderr, "Wrong key usage: %u\n",
286
result->wrong_key_usage);
287
if(result->file_name != NULL){
288
fprintf(stderr, "File name: %s\n", result->file_name);
290
gpgme_recipient_t recipient;
291
recipient = result->recipients;
293
while(recipient != NULL){
294
fprintf(stderr, "Public key algorithm: %s\n",
295
gpgme_pubkey_algo_name(recipient->pubkey_algo));
296
fprintf(stderr, "Key ID: %s\n", recipient->keyid);
297
fprintf(stderr, "Secret key available: %s\n",
298
recipient->status == GPG_ERR_NO_SECKEY
300
recipient = recipient->next;
309
fprintf(stderr, "Decryption of OpenPGP data succeeded\n");
147
/* gpgme_decrypt_result_t result; */
148
/* result = gpgme_op_decrypt_result(ctx); */
149
/* fprintf(stderr, "Unsupported algorithm: %s\n", result->unsupported_algorithm); */
150
/* fprintf(stderr, "Wrong key usage: %d\n", result->wrong_key_usage); */
151
/* if(result->file_name != NULL){ */
152
/* fprintf(stderr, "File name: %s\n", result->file_name); */
154
/* gpgme_recipient_t recipient; */
155
/* recipient = result->recipients; */
157
/* while(recipient != NULL){ */
158
/* fprintf(stderr, "Public key algorithm: %s\n", */
159
/* gpgme_pubkey_algo_name(recipient->pubkey_algo)); */
160
/* fprintf(stderr, "Key ID: %s\n", recipient->keyid); */
161
/* fprintf(stderr, "Secret key available: %s\n", */
162
/* recipient->status == GPG_ERR_NO_SECKEY ? "No" : "Yes"); */
163
/* recipient = recipient->next; */
167
/* Delete the GPGME FILE pointer cryptotext data buffer */
168
gpgme_data_release(dh_crypto);
312
170
/* Seek back to the beginning of the GPGME plaintext data buffer */
313
if (gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET) == -1){
314
perror("gpgme_data_seek");
315
plaintext_length = -1;
171
gpgme_data_seek(dh_plain, 0, SEEK_SET);
321
plaintext_capacity = adjustbuffer(plaintext,
322
(size_t)plaintext_length,
324
if (plaintext_capacity == 0){
325
perror("adjustbuffer");
326
plaintext_length = -1;
175
if (new_packet_length + BUFFER_SIZE > new_packet_capacity){
176
*new_packet = realloc(*new_packet, new_packet_capacity + BUFFER_SIZE);
177
if (*new_packet == NULL){
181
new_packet_capacity += BUFFER_SIZE;
330
ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
184
ret = gpgme_data_read(dh_plain, *new_packet + new_packet_length, BUFFER_SIZE);
332
185
/* Print the data, if any */
187
/* If password is empty, then a incorrect error will be printed */
338
191
perror("gpgme_data_read");
339
plaintext_length = -1;
342
plaintext_length += ret;
346
fprintf(stderr, "Decrypted password is: ");
347
for(ssize_t i = 0; i < plaintext_length; i++){
348
fprintf(stderr, "%02hhX ", (*plaintext)[i]);
350
fprintf(stderr, "\n");
355
/* Delete the GPGME cryptotext data buffer */
356
gpgme_data_release(dh_crypto);
358
/* Delete the GPGME plaintext data buffer */
194
new_packet_length += ret;
197
/* Delete the GPGME plaintext data buffer */
359
198
gpgme_data_release(dh_plain);
360
return plaintext_length;
199
return new_packet_length;
363
202
static const char * safer_gnutls_strerror (int value) {
364
const char *ret = gnutls_strerror (value); /* Spurious warning */
203
const char *ret = gnutls_strerror (value);
366
205
ret = "(unknown)";
370
/* GnuTLS log function callback */
371
static void debuggnutls(__attribute__((unused)) int level,
373
fprintf(stderr, "GnuTLS: %s", string);
209
void debuggnutls(int level, const char* string){
210
fprintf(stderr, "%s", string);
376
static int init_gnutls_global(mandos_context *mc,
377
const char *pubkeyfilename,
378
const char *seckeyfilename){
213
int initgnutls(encrypted_session *es){
382
fprintf(stderr, "Initializing GnuTLS\n");
385
ret = gnutls_global_init();
386
if (ret != GNUTLS_E_SUCCESS) {
387
fprintf (stderr, "GnuTLS global_init: %s\n",
388
safer_gnutls_strerror(ret));
393
/* "Use a log level over 10 to enable all debugging options."
396
gnutls_global_set_log_level(11);
397
gnutls_global_set_log_function(debuggnutls);
400
/* OpenPGP credentials */
401
gnutls_certificate_allocate_credentials(&mc->cred);
402
if (ret != GNUTLS_E_SUCCESS){
403
fprintf (stderr, "GnuTLS memory error: %s\n", /* Spurious
405
safer_gnutls_strerror(ret));
406
gnutls_global_deinit ();
411
fprintf(stderr, "Attempting to use OpenPGP public key %s and"
412
" secret key %s as GnuTLS credentials\n", pubkeyfilename,
217
if ((ret = gnutls_global_init ())
218
!= GNUTLS_E_SUCCESS) {
219
fprintf (stderr, "global_init: %s\n", safer_gnutls_strerror(ret));
223
/* Uncomment to enable full debuggin on the gnutls library */
224
/* gnutls_global_set_log_level(11); */
225
/* gnutls_global_set_log_function(debuggnutls); */
228
/* openpgp credentials */
229
if ((ret = gnutls_certificate_allocate_credentials (&es->cred))
230
!= GNUTLS_E_SUCCESS) {
231
fprintf (stderr, "memory error: %s\n", safer_gnutls_strerror(ret));
416
235
ret = gnutls_certificate_set_openpgp_key_file
417
(mc->cred, pubkeyfilename, seckeyfilename,
418
GNUTLS_OPENPGP_FMT_BASE64);
419
if (ret != GNUTLS_E_SUCCESS) {
421
"Error[%d] while reading the OpenPGP key pair ('%s',"
422
" '%s')\n", ret, pubkeyfilename, seckeyfilename);
423
fprintf(stderr, "The GnuTLS error is: %s\n",
424
safer_gnutls_strerror(ret));
428
/* GnuTLS server initialization */
429
ret = gnutls_dh_params_init(&mc->dh_params);
430
if (ret != GNUTLS_E_SUCCESS) {
431
fprintf (stderr, "Error in GnuTLS DH parameter initialization:"
432
" %s\n", safer_gnutls_strerror(ret));
435
ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
436
if (ret != GNUTLS_E_SUCCESS) {
437
fprintf (stderr, "Error in GnuTLS prime generation: %s\n",
438
safer_gnutls_strerror(ret));
442
gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
448
gnutls_certificate_free_credentials(mc->cred);
449
gnutls_global_deinit();
450
gnutls_dh_params_deinit(mc->dh_params);
454
static int init_gnutls_session(mandos_context *mc,
455
gnutls_session_t *session){
457
/* GnuTLS session creation */
458
ret = gnutls_init(session, GNUTLS_SERVER);
459
if (ret != GNUTLS_E_SUCCESS){
460
fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
461
safer_gnutls_strerror(ret));
466
ret = gnutls_priority_set_direct(*session, mc->priority, &err);
467
if (ret != GNUTLS_E_SUCCESS) {
468
fprintf(stderr, "Syntax error at: %s\n", err);
469
fprintf(stderr, "GnuTLS error: %s\n",
470
safer_gnutls_strerror(ret));
471
gnutls_deinit (*session);
476
ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
478
if (ret != GNUTLS_E_SUCCESS) {
479
fprintf(stderr, "Error setting GnuTLS credentials: %s\n",
480
safer_gnutls_strerror(ret));
481
gnutls_deinit (*session);
236
(es->cred, CERTFILE, KEYFILE, GNUTLS_OPENPGP_FMT_BASE64);
237
if (ret != GNUTLS_E_SUCCESS) {
239
(stderr, "Error[%d] while reading the OpenPGP key pair ('%s', '%s')\n",
240
ret, CERTFILE, KEYFILE);
241
fprintf(stdout, "The Error is: %s\n",
242
safer_gnutls_strerror(ret));
246
//Gnutls server initialization
247
if ((ret = gnutls_dh_params_init (&es->dh_params))
248
!= GNUTLS_E_SUCCESS) {
249
fprintf (stderr, "Error in dh parameter initialization: %s\n",
250
safer_gnutls_strerror(ret));
254
if ((ret = gnutls_dh_params_generate2 (es->dh_params, DH_BITS))
255
!= GNUTLS_E_SUCCESS) {
256
fprintf (stderr, "Error in prime generation: %s\n",
257
safer_gnutls_strerror(ret));
261
gnutls_certificate_set_dh_params (es->cred, es->dh_params);
263
// Gnutls session creation
264
if ((ret = gnutls_init (&es->session, GNUTLS_SERVER))
265
!= GNUTLS_E_SUCCESS){
266
fprintf(stderr, "Error in gnutls session initialization: %s\n",
267
safer_gnutls_strerror(ret));
270
if ((ret = gnutls_priority_set_direct (es->session, "NORMAL", &err))
271
!= GNUTLS_E_SUCCESS) {
272
fprintf(stderr, "Syntax error at: %s\n", err);
273
fprintf(stderr, "Gnutls error: %s\n",
274
safer_gnutls_strerror(ret));
278
if ((ret = gnutls_credentials_set
279
(es->session, GNUTLS_CRD_CERTIFICATE, es->cred))
280
!= GNUTLS_E_SUCCESS) {
281
fprintf(stderr, "Error setting a credentials set: %s\n",
282
safer_gnutls_strerror(ret));
485
286
/* ignore client certificate if any. */
486
gnutls_certificate_server_set_request (*session,
287
gnutls_certificate_server_set_request (es->session, GNUTLS_CERT_IGNORE);
489
gnutls_dh_set_prime_bits (*session, mc->dh_bits);
289
gnutls_dh_set_prime_bits (es->session, DH_BITS);
494
/* Avahi log function callback */
495
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
496
__attribute__((unused)) const char *txt){}
294
void empty_log(AvahiLogLevel level, const char *txt){}
498
/* Called when a Mandos server is found */
499
static int start_mandos_communication(const char *ip, uint16_t port,
500
AvahiIfIndex if_index,
296
int start_mandos_communcation(char *ip, uint16_t port){
503
union { struct sockaddr in; struct sockaddr_in6 in6; } to;
298
struct sockaddr_in6 to;
299
struct in6_addr ip_addr;
300
encrypted_session es;
504
301
char *buffer = NULL;
505
302
char *decrypted_buffer;
506
303
size_t buffer_length = 0;
507
304
size_t buffer_capacity = 0;
508
305
ssize_t decrypted_buffer_size;
511
char interface[IF_NAMESIZE];
512
gnutls_session_t session;
514
ret = init_gnutls_session (mc, &session);
520
fprintf(stderr, "Setting up a tcp connection to %s, port %" PRIu16
524
309
tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
531
if(if_indextoname((unsigned int)if_index, interface) == NULL){
532
perror("if_indextoname");
535
fprintf(stderr, "Binding to interface %s\n", interface);
315
ret = setsockopt(tcp_sd, SOL_SOCKET, SO_BINDTODEVICE, "eth0", 5);
317
perror("setsockopt bindtodevice");
538
memset(&to, 0, sizeof(to));
539
to.in6.sin6_family = AF_INET6;
540
/* It would be nice to have a way to detect if we were passed an
541
IPv4 address here. Now we assume an IPv6 address. */
542
ret = inet_pton(AF_INET6, ip, &to.in6.sin6_addr);
321
memset(&to,0,sizeof(to));
322
to.sin6_family = AF_INET6;
323
ret = inet_pton(AF_INET6, ip, &ip_addr);
544
325
perror("inet_pton");
548
329
fprintf(stderr, "Bad address: %s\n", ip);
551
to.in6.sin6_port = htons(port); /* Spurious warning */
553
to.in6.sin6_scope_id = (uint32_t)if_index;
556
fprintf(stderr, "Connection to: %s, port %" PRIu16 "\n", ip,
558
char addrstr[INET6_ADDRSTRLEN] = "";
559
if(inet_ntop(to.in6.sin6_family, &(to.in6.sin6_addr), addrstr,
560
sizeof(addrstr)) == NULL){
563
if(strcmp(addrstr, ip) != 0){
564
fprintf(stderr, "Canonical address form: %s\n", addrstr);
569
ret = connect(tcp_sd, &to.in, sizeof(to));
332
to.sin6_port = htons(port);
333
to.sin6_scope_id = if_nametoindex("eth0");
335
ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
571
337
perror("connect");
575
const char *out = mandos_protocol_version;
578
size_t out_size = strlen(out);
579
ret = TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
580
out_size - written));
586
written += (size_t)ret;
587
if(written < out_size){
590
if (out == mandos_protocol_version){
600
fprintf(stderr, "Establishing TLS session with %s\n", ip);
603
gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t) tcp_sd);
606
ret = gnutls_handshake (session);
607
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
341
ret = initgnutls (&es);
348
gnutls_transport_set_ptr (es.session, (gnutls_transport_ptr_t) tcp_sd);
350
ret = gnutls_handshake (es.session);
609
352
if (ret != GNUTLS_E_SUCCESS){
611
fprintf(stderr, "*** GnuTLS Handshake failed ***\n");
353
fprintf(stderr, "\n*** Handshake failed ***\n");
618
/* Read OpenPGP packet that contains the wanted password */
621
fprintf(stderr, "Retrieving pgp encrypted password from %s\n",
626
buffer_capacity = adjustbuffer(&buffer, buffer_length,
628
if (buffer_capacity == 0){
629
perror("adjustbuffer");
361
if (buffer_length + BUFFER_SIZE > buffer_capacity){
362
buffer = realloc(buffer, buffer_capacity + BUFFER_SIZE);
367
buffer_capacity += BUFFER_SIZE;
634
ret = gnutls_record_recv(session, buffer+buffer_length,
370
ret = gnutls_record_recv
371
(es.session, buffer+buffer_length, BUFFER_SIZE);
642
378
case GNUTLS_E_AGAIN:
644
380
case GNUTLS_E_REHANDSHAKE:
646
ret = gnutls_handshake (session);
647
} while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
381
ret = gnutls_handshake (es.session);
649
fprintf(stderr, "*** GnuTLS Re-handshake failed ***\n");
383
fprintf(stderr, "\n*** Handshake failed ***\n");
650
384
gnutls_perror (ret);
656
fprintf(stderr, "Unknown error while reading data from"
657
" encrypted session with Mandos server\n");
390
fprintf(stderr, "Unknown error while reading data from encrypted session with mandos server\n");
659
gnutls_bye (session, GNUTLS_SHUT_RDWR);
392
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
663
buffer_length += (size_t) ret;
396
buffer_length += ret;
668
fprintf(stderr, "Closing TLS session\n");
671
gnutls_bye (session, GNUTLS_SHUT_RDWR);
673
400
if (buffer_length > 0){
674
decrypted_buffer_size = pgp_packet_decrypt(mc, buffer,
677
if (decrypted_buffer_size >= 0){
679
while(written < (size_t) decrypted_buffer_size){
680
ret = (int)fwrite (decrypted_buffer + written, 1,
681
(size_t)decrypted_buffer_size - written,
683
if(ret == 0 and ferror(stdout)){
685
fprintf(stderr, "Error writing encrypted data: %s\n",
691
written += (size_t)ret;
401
if ((decrypted_buffer_size = gpg_packet_decrypt(buffer, buffer_length, &decrypted_buffer, CERT_ROOT)) == 0){
404
fwrite (decrypted_buffer, 1, decrypted_buffer_size, stdout);
693
405
free(decrypted_buffer);
701
/* Shutdown procedure */
705
ret = TEMP_FAILURE_RETRY(close(tcp_sd));
709
gnutls_deinit (session);
412
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
415
gnutls_deinit (es.session);
416
gnutls_certificate_free_credentials (es.cred);
417
gnutls_global_deinit ();
713
static void resolve_callback(AvahiSServiceResolver *r,
714
AvahiIfIndex interface,
715
AVAHI_GCC_UNUSED AvahiProtocol protocol,
716
AvahiResolverEvent event,
720
const char *host_name,
721
const AvahiAddress *address,
723
AVAHI_GCC_UNUSED AvahiStringList *txt,
724
AVAHI_GCC_UNUSED AvahiLookupResultFlags
727
mandos_context *mc = userdata;
730
/* Called whenever a service has been resolved successfully or
735
case AVAHI_RESOLVER_FAILURE:
736
fprintf(stderr, "(Avahi Resolver) Failed to resolve service '%s'"
737
" of type '%s' in domain '%s': %s\n", name, type, domain,
738
avahi_strerror(avahi_server_errno(mc->server)));
741
case AVAHI_RESOLVER_FOUND:
743
char ip[AVAHI_ADDRESS_STR_MAX];
744
avahi_address_snprint(ip, sizeof(ip), address);
746
fprintf(stderr, "Mandos server \"%s\" found on %s (%s, %"
747
PRIu16 ") on port %d\n", name, host_name, ip,
750
int ret = start_mandos_communication(ip, port, interface, mc);
752
avahi_simple_poll_quit(mc->simple_poll);
756
avahi_s_service_resolver_free(r);
759
static void browse_callback( AvahiSServiceBrowser *b,
760
AvahiIfIndex interface,
761
AvahiProtocol protocol,
762
AvahiBrowserEvent event,
766
AVAHI_GCC_UNUSED AvahiLookupResultFlags
769
mandos_context *mc = userdata;
772
/* Called whenever a new services becomes available on the LAN or
773
is removed from the LAN */
777
case AVAHI_BROWSER_FAILURE:
779
fprintf(stderr, "(Avahi browser) %s\n",
780
avahi_strerror(avahi_server_errno(mc->server)));
781
avahi_simple_poll_quit(mc->simple_poll);
784
case AVAHI_BROWSER_NEW:
785
/* We ignore the returned Avahi resolver object. In the callback
786
function we free it. If the Avahi server is terminated before
787
the callback function is called the Avahi server will free the
790
if (!(avahi_s_service_resolver_new(mc->server, interface,
791
protocol, name, type, domain,
792
AVAHI_PROTO_INET6, 0,
793
resolve_callback, mc)))
794
fprintf(stderr, "Avahi: Failed to resolve service '%s': %s\n",
795
name, avahi_strerror(avahi_server_errno(mc->server)));
798
case AVAHI_BROWSER_REMOVE:
801
case AVAHI_BROWSER_ALL_FOR_NOW:
802
case AVAHI_BROWSER_CACHE_EXHAUSTED:
804
fprintf(stderr, "No Mandos server found, still searching...\n");
810
int main(int argc, char *argv[]){
421
static AvahiSimplePoll *simple_poll = NULL;
422
static AvahiServer *server = NULL;
424
static void resolve_callback(
425
AvahiSServiceResolver *r,
426
AVAHI_GCC_UNUSED AvahiIfIndex interface,
427
AVAHI_GCC_UNUSED AvahiProtocol protocol,
428
AvahiResolverEvent event,
432
const char *host_name,
433
const AvahiAddress *address,
435
AvahiStringList *txt,
436
AvahiLookupResultFlags flags,
437
AVAHI_GCC_UNUSED void* userdata) {
441
/* Called whenever a service has been resolved successfully or timed out */
444
case AVAHI_RESOLVER_FAILURE:
445
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)));
448
case AVAHI_RESOLVER_FOUND: {
449
char ip[AVAHI_ADDRESS_STR_MAX];
450
avahi_address_snprint(ip, sizeof(ip), address);
451
int ret = start_mandos_communcation(ip, port);
459
avahi_s_service_resolver_free(r);
462
static void browse_callback(
463
AvahiSServiceBrowser *b,
464
AvahiIfIndex interface,
465
AvahiProtocol protocol,
466
AvahiBrowserEvent event,
470
AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
473
AvahiServer *s = userdata;
476
/* Called whenever a new services becomes available on the LAN or is removed from the LAN */
480
case AVAHI_BROWSER_FAILURE:
482
fprintf(stderr, "(Browser) %s\n", avahi_strerror(avahi_server_errno(server)));
483
avahi_simple_poll_quit(simple_poll);
486
case AVAHI_BROWSER_NEW:
487
/* We ignore the returned resolver object. In the callback
488
function we free it. If the server is terminated before
489
the callback function is called the server will free
490
the resolver for us. */
492
if (!(avahi_s_service_resolver_new(s, interface, protocol, name, type, domain, AVAHI_PROTO_INET6, 0, resolve_callback, s)))
493
fprintf(stderr, "Failed to resolve service '%s': %s\n", name, avahi_strerror(avahi_server_errno(s)));
497
case AVAHI_BROWSER_REMOVE:
500
case AVAHI_BROWSER_ALL_FOR_NOW:
501
case AVAHI_BROWSER_CACHE_EXHAUSTED:
506
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
507
AvahiServerConfig config;
811
508
AvahiSServiceBrowser *sb = NULL;
814
int exitcode = EXIT_SUCCESS;
815
const char *interface = "eth0";
816
struct ifreq network;
820
char *connect_to = NULL;
821
char tempdir[] = "/tmp/mandosXXXXXX";
822
AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
823
const char *seckey = PATHDIR "/" SECKEY;
824
const char *pubkey = PATHDIR "/" PUBKEY;
826
mandos_context mc = { .simple_poll = NULL, .server = NULL,
827
.dh_bits = 1024, .priority = "SECURE256"
828
":!CTYPE-X.509:+CTYPE-OPENPGP" };
829
bool gnutls_initalized = false;
830
bool gpgme_initalized = false;
833
struct argp_option options[] = {
834
{ .name = "debug", .key = 128,
835
.doc = "Debug mode", .group = 3 },
836
{ .name = "connect", .key = 'c',
837
.arg = "ADDRESS:PORT",
838
.doc = "Connect directly to a specific Mandos server",
840
{ .name = "interface", .key = 'i',
842
.doc = "Interface that will be used to search for Mandos"
845
{ .name = "seckey", .key = 's',
847
.doc = "OpenPGP secret key file base name",
849
{ .name = "pubkey", .key = 'p',
851
.doc = "OpenPGP public key file base name",
853
{ .name = "dh-bits", .key = 129,
855
.doc = "Bit length of the prime number used in the"
856
" Diffie-Hellman key exchange",
858
{ .name = "priority", .key = 130,
860
.doc = "GnuTLS priority string for the TLS handshake",
865
error_t parse_opt (int key, char *arg,
866
struct argp_state *state) {
867
/* Get the INPUT argument from `argp_parse', which we know is
868
a pointer to our plugin list pointer. */
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 */
887
mc.dh_bits = (unsigned int) strtol(arg, NULL, 10);
893
case 130: /* --priority */
901
return ARGP_ERR_UNKNOWN;
906
struct argp argp = { .options = options, .parser = parse_opt,
908
.doc = "Mandos client -- Get and decrypt"
909
" passwords from a Mandos server" };
910
ret = argp_parse (&argp, argc, argv, 0, 0, NULL);
911
if (ret == ARGP_ERR_UNKNOWN){
912
fprintf(stderr, "Unknown error while parsing arguments\n");
913
exitcode = EXIT_FAILURE;
918
/* If the interface is down, bring it up */
920
sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
923
exitcode = EXIT_FAILURE;
926
strcpy(network.ifr_name, interface);
927
ret = ioctl(sd, SIOCGIFFLAGS, &network);
929
perror("ioctl SIOCGIFFLAGS");
930
exitcode = EXIT_FAILURE;
933
if((network.ifr_flags & IFF_UP) == 0){
934
network.ifr_flags |= IFF_UP;
935
ret = ioctl(sd, SIOCSIFFLAGS, &network);
937
perror("ioctl SIOCSIFFLAGS");
938
exitcode = EXIT_FAILURE;
942
ret = TEMP_FAILURE_RETRY(close(sd));
961
ret = init_gnutls_global(&mc, pubkey, seckey);
963
fprintf(stderr, "init_gnutls_global failed\n");
964
exitcode = EXIT_FAILURE;
967
gnutls_initalized = true;
970
if(mkdtemp(tempdir) == NULL){
976
if(not init_gpgme(&mc, pubkey, seckey, tempdir)){
977
fprintf(stderr, "gpgme_initalized failed\n");
978
exitcode = EXIT_FAILURE;
981
gpgme_initalized = true;
984
if_index = (AvahiIfIndex) if_nametoindex(interface);
986
fprintf(stderr, "No such interface: \"%s\"\n", interface);
990
if(connect_to != NULL){
991
/* Connect directly, do not use Zeroconf */
992
/* (Mainly meant for debugging) */
993
char *address = strrchr(connect_to, ':');
995
fprintf(stderr, "No colon in address\n");
996
exitcode = EXIT_FAILURE;
1000
uint16_t port = (uint16_t) strtol(address+1, NULL, 10);
1002
perror("Bad port number");
1003
exitcode = EXIT_FAILURE;
1007
address = connect_to;
1008
ret = start_mandos_communication(address, port, if_index, &mc);
1010
exitcode = EXIT_FAILURE;
1012
exitcode = EXIT_SUCCESS;
1018
avahi_set_log_function(empty_log);
1021
/* Initialize the pseudo-RNG for Avahi */
1022
srand((unsigned int) time(NULL));
1024
/* Allocate main Avahi loop object */
1025
mc.simple_poll = avahi_simple_poll_new();
1026
if (mc.simple_poll == NULL) {
1027
fprintf(stderr, "Avahi: Failed to create simple poll"
1029
exitcode = EXIT_FAILURE;
1034
AvahiServerConfig config;
1035
/* Do not publish any local Zeroconf records */
1036
avahi_server_config_init(&config);
1037
config.publish_hinfo = 0;
1038
config.publish_addresses = 0;
1039
config.publish_workstation = 0;
1040
config.publish_domain = 0;
1042
/* Allocate a new server */
1043
mc.server = avahi_server_new(avahi_simple_poll_get
1044
(mc.simple_poll), &config, NULL,
1047
/* Free the Avahi configuration data */
1048
avahi_server_config_free(&config);
1051
/* Check if creating the Avahi server object succeeded */
1052
if (mc.server == NULL) {
1053
fprintf(stderr, "Failed to create Avahi server: %s\n",
1054
avahi_strerror(error));
1055
exitcode = EXIT_FAILURE;
1059
/* Create the Avahi service browser */
1060
sb = avahi_s_service_browser_new(mc.server, if_index,
1062
"_mandos._tcp", NULL, 0,
1063
browse_callback, &mc);
1065
fprintf(stderr, "Failed to create service browser: %s\n",
1066
avahi_strerror(avahi_server_errno(mc.server)));
1067
exitcode = EXIT_FAILURE;
512
avahi_set_log_function(empty_log);
514
/* Initialize the psuedo-RNG */
517
/* Allocate main loop object */
518
if (!(simple_poll = avahi_simple_poll_new())) {
519
fprintf(stderr, "Failed to create simple poll object.\n");
523
/* Do not publish any local records */
524
avahi_server_config_init(&config);
525
config.publish_hinfo = 0;
526
config.publish_addresses = 0;
527
config.publish_workstation = 0;
528
config.publish_domain = 0;
530
/* /\* Set a unicast DNS server for wide area DNS-SD *\/ */
531
/* avahi_address_parse("193.11.177.11", AVAHI_PROTO_UNSPEC, &config.wide_area_servers[0]); */
532
/* config.n_wide_area_servers = 1; */
533
/* config.enable_wide_area = 1; */
535
/* Allocate a new server */
536
server = avahi_server_new(avahi_simple_poll_get(simple_poll), &config, NULL, NULL, &error);
538
/* Free the configuration data */
539
avahi_server_config_free(&config);
541
/* Check wether creating the server object succeeded */
543
fprintf(stderr, "Failed to create server: %s\n", avahi_strerror(error));
547
/* Create the service browser */
548
if (!(sb = avahi_s_service_browser_new(server, if_nametoindex("eth0"), AVAHI_PROTO_INET6, "_mandos._tcp", NULL, 0, browse_callback, server))) {
549
fprintf(stderr, "Failed to create service browser: %s\n", avahi_strerror(avahi_server_errno(server)));
1071
553
/* Run the main loop */
1074
fprintf(stderr, "Starting Avahi loop search\n");
1077
avahi_simple_poll_loop(mc.simple_poll);
1082
fprintf(stderr, "%s exiting\n", argv[0]);
554
avahi_simple_poll_loop(simple_poll);
1085
560
/* Cleanup things */
1087
562
avahi_s_service_browser_free(sb);
1089
if (mc.server != NULL)
1090
avahi_server_free(mc.server);
1092
if (mc.simple_poll != NULL)
1093
avahi_simple_poll_free(mc.simple_poll);
1095
if (gnutls_initalized){
1096
gnutls_certificate_free_credentials(mc.cred);
1097
gnutls_global_deinit ();
1098
gnutls_dh_params_deinit(mc.dh_params);
1101
if(gpgme_initalized){
1102
gpgme_release(mc.ctx);
1105
/* Removes the temp directory used by GPGME */
1106
if(tempdir[0] != '\0'){
1108
struct dirent *direntry;
1109
d = opendir(tempdir);
1114
direntry = readdir(d);
1115
if(direntry == NULL){
1118
if (direntry->d_type == DT_REG){
1119
char *fullname = NULL;
1120
ret = asprintf(&fullname, "%s/%s", tempdir,
1126
ret = unlink(fullname);
1128
fprintf(stderr, "unlink(\"%s\"): %s",
1129
fullname, strerror(errno));
1136
ret = rmdir(tempdir);
565
avahi_server_free(server);
568
avahi_simple_poll_free(simple_poll);