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 © 2007-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 <https://www.fukt.bsnet.se/~belorn/> and
29
* <https://www.fukt.bsnet.se/~teddy/>.
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
32
/* Needed by GPGME, specifically gpgme_data_seek() */
33
20
#define _LARGEFILE_SOURCE
34
21
#define _FILE_OFFSET_BITS 64
47
34
#include <avahi-common/error.h>
49
36
//mandos client part
50
#include <sys/types.h> /* socket(), inet_pton() */
51
#include <sys/socket.h> /* socket(), struct sockaddr_in6,
52
struct in6_addr, inet_pton() */
53
#include <gnutls/gnutls.h> /* All GnuTLS stuff */
54
#include <gnutls/openpgp.h> /* GnuTLS with openpgp stuff */
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 */
56
42
#include <unistd.h> /* close() */
57
43
#include <netinet/in.h>
84
71
} encrypted_session;
87
ssize_t pgp_packet_decrypt (char *packet, size_t packet_size,
88
char **new_packet, const char *homedir){
74
ssize_t gpg_packet_decrypt (char *packet, size_t packet_size, char **new_packet, char *homedir){
89
75
gpgme_data_t dh_crypto, dh_plain;
93
ssize_t new_packet_capacity = 0;
94
ssize_t new_packet_length = 0;
79
size_t new_packet_capacity = 0;
80
size_t new_packet_length = 0;
95
81
gpgme_engine_info_t engine_info;
98
fprintf(stderr, "Trying to decrypt OpenPGP packet\n");
84
fprintf(stderr, "Attempting to decrypt password from gpg packet\n");
165
150
if (result == NULL){
166
151
fprintf(stderr, "gpgme_op_decrypt_result failed\n");
168
fprintf(stderr, "Unsupported algorithm: %s\n",
169
result->unsupported_algorithm);
170
fprintf(stderr, "Wrong key usage: %d\n",
171
result->wrong_key_usage);
153
fprintf(stderr, "Unsupported algorithm: %s\n", result->unsupported_algorithm);
154
fprintf(stderr, "Wrong key usage: %d\n", result->wrong_key_usage);
172
155
if(result->file_name != NULL){
173
156
fprintf(stderr, "File name: %s\n", result->file_name);
192
174
gpgme_data_release(dh_crypto);
194
176
/* Seek back to the beginning of the GPGME plaintext data buffer */
195
gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET);
177
gpgme_data_seek(dh_plain, 0, SEEK_SET);
199
181
if (new_packet_length + BUFFER_SIZE > new_packet_capacity){
200
*new_packet = realloc(*new_packet,
201
(unsigned int)new_packet_capacity
182
*new_packet = realloc(*new_packet, new_packet_capacity + BUFFER_SIZE);
203
183
if (*new_packet == NULL){
204
184
perror("realloc");
243
void debuggnutls(__attribute__((unused)) int level,
223
void debuggnutls(int level, const char* string){
245
224
fprintf(stderr, "%s", string);
248
227
int initgnutls(encrypted_session *es){
253
fprintf(stderr, "Initializing GnuTLS\n");
232
fprintf(stderr, "Initializing gnutls\n");
256
236
if ((ret = gnutls_global_init ())
257
237
!= GNUTLS_E_SUCCESS) {
264
244
gnutls_global_set_log_function(debuggnutls);
267
248
/* openpgp credentials */
268
249
if ((ret = gnutls_certificate_allocate_credentials (&es->cred))
269
250
!= GNUTLS_E_SUCCESS) {
270
fprintf (stderr, "memory error: %s\n",
271
safer_gnutls_strerror(ret));
251
fprintf (stderr, "memory error: %s\n", safer_gnutls_strerror(ret));
276
fprintf(stderr, "Attempting to use OpenPGP certificate %s"
277
" and keyfile %s as GnuTLS credentials\n", CERTFILE,
256
fprintf(stderr, "Attempting to use openpgp certificate %s"
257
" and keyfile %s as gnutls credentials\n", CERTFILE, KEYFILE);
281
260
ret = gnutls_certificate_set_openpgp_key_file
282
261
(es->cred, CERTFILE, KEYFILE, GNUTLS_OPENPGP_FMT_BASE64);
283
262
if (ret != GNUTLS_E_SUCCESS) {
285
(stderr, "Error[%d] while reading the OpenPGP key pair ('%s',"
264
(stderr, "Error[%d] while reading the OpenPGP key pair ('%s', '%s')\n",
287
265
ret, CERTFILE, KEYFILE);
288
266
fprintf(stdout, "The Error is: %s\n",
289
267
safer_gnutls_strerror(ret));
293
//GnuTLS server initialization
271
//Gnutls server initialization
294
272
if ((ret = gnutls_dh_params_init (&es->dh_params))
295
273
!= GNUTLS_E_SUCCESS) {
296
274
fprintf (stderr, "Error in dh parameter initialization: %s\n",
297
275
safer_gnutls_strerror(ret));
301
279
if ((ret = gnutls_dh_params_generate2 (es->dh_params, DH_BITS))
302
280
!= GNUTLS_E_SUCCESS) {
303
281
fprintf (stderr, "Error in prime generation: %s\n",
304
282
safer_gnutls_strerror(ret));
308
286
gnutls_certificate_set_dh_params (es->cred, es->dh_params);
310
// GnuTLS session creation
288
// Gnutls session creation
311
289
if ((ret = gnutls_init (&es->session, GNUTLS_SERVER))
312
290
!= GNUTLS_E_SUCCESS){
313
fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
291
fprintf(stderr, "Error in gnutls session initialization: %s\n",
314
292
safer_gnutls_strerror(ret));
317
295
if ((ret = gnutls_priority_set_direct (es->session, "NORMAL", &err))
318
296
!= GNUTLS_E_SUCCESS) {
319
297
fprintf(stderr, "Syntax error at: %s\n", err);
320
fprintf(stderr, "GnuTLS error: %s\n",
298
fprintf(stderr, "Gnutls error: %s\n",
321
299
safer_gnutls_strerror(ret));
325
303
if ((ret = gnutls_credentials_set
326
304
(es->session, GNUTLS_CRD_CERTIFICATE, es->cred))
327
305
!= GNUTLS_E_SUCCESS) {
329
307
safer_gnutls_strerror(ret));
333
311
/* ignore client certificate if any. */
334
gnutls_certificate_server_set_request (es->session,
312
gnutls_certificate_server_set_request (es->session, GNUTLS_CERT_IGNORE);
337
314
gnutls_dh_set_prime_bits (es->session, DH_BITS);
342
void empty_log(__attribute__((unused)) AvahiLogLevel level,
343
__attribute__((unused)) const char *txt){}
319
void empty_log(AvahiLogLevel level, const char *txt){}
345
int start_mandos_communication(const char *ip, uint16_t port,
346
unsigned int if_index){
321
int start_mandos_communcation(char *ip, uint16_t port){
348
323
struct sockaddr_in6 to;
324
struct in6_addr ip_addr;
349
325
encrypted_session es;
350
326
char *buffer = NULL;
351
327
char *decrypted_buffer;
352
328
size_t buffer_length = 0;
353
329
size_t buffer_capacity = 0;
354
330
ssize_t decrypted_buffer_size;
357
char interface[IF_NAMESIZE];
360
fprintf(stderr, "Setting up a tcp connection to %s, port %d\n",
334
fprintf(stderr, "Setting up a tcp connection to %s\n", ip);
364
337
tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
366
339
perror("socket");
370
if(if_indextoname(if_index, interface) == NULL){
372
perror("if_indextoname");
378
344
fprintf(stderr, "Binding to interface %s\n", interface);
347
ret = setsockopt(tcp_sd, SOL_SOCKET, SO_BINDTODEVICE, interface, 5);
349
perror("setsockopt bindtodevice");
381
memset(&to,0,sizeof(to)); /* Spurious warning */
353
memset(&to,0,sizeof(to));
382
354
to.sin6_family = AF_INET6;
383
ret = inet_pton(AF_INET6, ip, &to.sin6_addr);
355
ret = inet_pton(AF_INET6, ip, &ip_addr);
385
357
perror("inet_pton");
389
361
fprintf(stderr, "Bad address: %s\n", ip);
392
to.sin6_port = htons(port); /* Spurious warning */
394
to.sin6_scope_id = (uint32_t)if_index;
364
to.sin6_port = htons(port);
365
to.sin6_scope_id = if_nametoindex(interface);
397
fprintf(stderr, "Connection to: %s, port %d\n", ip, port);
398
/* char addrstr[INET6_ADDRSTRLEN]; */
399
/* if(inet_ntop(to.sin6_family, &(to.sin6_addr), addrstr, */
400
/* sizeof(addrstr)) == NULL){ */
401
/* perror("inet_ntop"); */
403
/* fprintf(stderr, "Really connecting to: %s, port %d\n", */
404
/* addrstr, ntohs(to.sin6_port)); */
368
fprintf(stderr, "Connection to: %s\n", ip);
408
371
ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
420
gnutls_transport_set_ptr (es.session,
421
(gnutls_transport_ptr_t) tcp_sd);
384
gnutls_transport_set_ptr (es.session, (gnutls_transport_ptr_t) tcp_sd);
424
fprintf(stderr, "Establishing TLS session with %s\n", ip);
387
fprintf(stderr, "Establishing tls session with %s\n", ip);
427
391
ret = gnutls_handshake (es.session);
429
393
if (ret != GNUTLS_E_SUCCESS){
431
fprintf(stderr, "\n*** Handshake failed ***\n");
394
fprintf(stderr, "\n*** Handshake failed ***\n");
438
//Retrieve OpenPGP packet that contains the wanted password
400
//Retrieve gpg packet that contains the wanted password
441
fprintf(stderr, "Retrieving pgp encrypted password from %s\n",
403
fprintf(stderr, "Retrieving pgp encrypted password from %s\n", ip);
475
fprintf(stderr, "Unknown error while reading data from"
476
" encrypted session with mandos server\n");
436
fprintf(stderr, "Unknown error while reading data from encrypted session with mandos server\n");
478
438
gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
482
buffer_length += (size_t) ret;
442
buffer_length += ret;
486
446
if (buffer_length > 0){
487
decrypted_buffer_size = pgp_packet_decrypt(buffer,
491
if (decrypted_buffer_size >= 0){
492
while(written < (size_t) decrypted_buffer_size){
493
ret = (int)fwrite (decrypted_buffer + written, 1,
494
(size_t)decrypted_buffer_size - written,
496
if(ret == 0 and ferror(stdout)){
498
fprintf(stderr, "Error writing encrypted data: %s\n",
504
written += (size_t)ret;
447
if ((decrypted_buffer_size = gpg_packet_decrypt(buffer, buffer_length, &decrypted_buffer, CERT_ROOT)) >= 0){
448
fwrite (decrypted_buffer, 1, decrypted_buffer_size, stdout);
506
449
free(decrypted_buffer);
539
482
const char *host_name,
540
483
const AvahiAddress *address,
542
AVAHI_GCC_UNUSED AvahiStringList *txt,
543
AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
485
AvahiStringList *txt,
486
AvahiLookupResultFlags flags,
544
487
AVAHI_GCC_UNUSED void* userdata) {
546
assert(r); /* Spurious warning */
548
/* Called whenever a service has been resolved successfully or
553
case AVAHI_RESOLVER_FAILURE:
554
fprintf(stderr, "(Resolver) Failed to resolve service '%s' of"
555
" type '%s' in domain '%s': %s\n", name, type, domain,
556
avahi_strerror(avahi_server_errno(server)));
559
case AVAHI_RESOLVER_FOUND:
561
char ip[AVAHI_ADDRESS_STR_MAX];
562
avahi_address_snprint(ip, sizeof(ip), address);
564
fprintf(stderr, "Mandos server \"%s\" found on %s (%s) on"
565
" port %d\n", name, host_name, ip, port);
567
int ret = start_mandos_communication(ip, port,
568
(unsigned int) interface);
491
/* Called whenever a service has been resolved successfully or timed out */
494
case AVAHI_RESOLVER_FAILURE:
495
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)));
498
case AVAHI_RESOLVER_FOUND: {
499
char ip[AVAHI_ADDRESS_STR_MAX];
500
avahi_address_snprint(ip, sizeof(ip), address);
502
fprintf(stderr, "Mandos server found at %s on port %d\n", ip, port);
504
int ret = start_mandos_communcation(ip, port);
574
avahi_s_service_resolver_free(r);
512
avahi_s_service_resolver_free(r);
577
515
static void browse_callback(
586
524
void* userdata) {
588
526
AvahiServer *s = userdata;
589
assert(b); /* Spurious warning */
591
/* Called whenever a new services becomes available on the LAN or
592
is removed from the LAN */
529
/* Called whenever a new services becomes available on the LAN or is removed from the LAN */
596
case AVAHI_BROWSER_FAILURE:
598
fprintf(stderr, "(Browser) %s\n",
599
avahi_strerror(avahi_server_errno(server)));
600
avahi_simple_poll_quit(simple_poll);
603
case AVAHI_BROWSER_NEW:
604
/* We ignore the returned resolver object. In the callback
605
function we free it. If the server is terminated before
606
the callback function is called the server will free
607
the resolver for us. */
609
if (!(avahi_s_service_resolver_new(s, interface, protocol, name,
611
AVAHI_PROTO_INET6, 0,
612
resolve_callback, s)))
613
fprintf(stderr, "Failed to resolve service '%s': %s\n", name,
614
avahi_strerror(avahi_server_errno(s)));
617
case AVAHI_BROWSER_REMOVE:
620
case AVAHI_BROWSER_ALL_FOR_NOW:
621
case AVAHI_BROWSER_CACHE_EXHAUSTED:
533
case AVAHI_BROWSER_FAILURE:
535
fprintf(stderr, "(Browser) %s\n", avahi_strerror(avahi_server_errno(server)));
536
avahi_simple_poll_quit(simple_poll);
539
case AVAHI_BROWSER_NEW:
540
/* We ignore the returned resolver object. In the callback
541
function we free it. If the server is terminated before
542
the callback function is called the server will free
543
the resolver for us. */
545
if (!(avahi_s_service_resolver_new(s, interface, protocol, name, type, domain, AVAHI_PROTO_INET6, 0, resolve_callback, s)))
546
fprintf(stderr, "Failed to resolve service '%s': %s\n", name, avahi_strerror(avahi_server_errno(s)));
550
case AVAHI_BROWSER_REMOVE:
553
case AVAHI_BROWSER_ALL_FOR_NOW:
554
case AVAHI_BROWSER_CACHE_EXHAUSTED:
631
564
int returncode = EXIT_SUCCESS;
632
const char *interface = "eth0";
633
unsigned int if_index;
634
char *connect_to = NULL;
637
567
static struct option long_options[] = {
638
568
{"debug", no_argument, (int *)&debug, 1},
639
{"connect", required_argument, 0, 'c'},
640
569
{"interface", required_argument, 0, 'i'},
643
572
int option_index = 0;
644
ret = getopt_long (argc, argv, "i:", long_options,
573
ret = getopt_long (argc, argv, "i:", long_options, &option_index);
655
583
interface = optarg;
661
586
exit(EXIT_FAILURE);
665
if_index = if_nametoindex(interface);
667
fprintf(stderr, "No such interface: \"%s\"\n", interface);
671
if(connect_to != NULL){
672
/* Connect directly, do not use Zeroconf */
673
/* (Mainly meant for debugging) */
674
char *address = strrchr(connect_to, ':');
676
fprintf(stderr, "No colon in address\n");
680
uint16_t port = (uint16_t) strtol(address+1, NULL, 10);
682
perror("Bad port number");
686
address = connect_to;
687
ret = start_mandos_communication(address, port, if_index);
696
591
avahi_set_log_function(empty_log);
699
594
/* Initialize the psuedo-RNG */
700
srand((unsigned int) time(NULL));
702
597
/* Allocate main loop object */
703
598
if (!(simple_poll = avahi_simple_poll_new())) {
714
609
config.publish_domain = 0;
716
611
/* Allocate a new server */
717
server = avahi_server_new(avahi_simple_poll_get(simple_poll),
718
&config, NULL, NULL, &error);
612
server = avahi_server_new(avahi_simple_poll_get(simple_poll), &config, NULL, NULL, &error);
720
614
/* Free the configuration data */
721
615
avahi_server_config_free(&config);
723
617
/* Check if creating the server object succeeded */
725
fprintf(stderr, "Failed to create server: %s\n",
726
avahi_strerror(error));
619
fprintf(stderr, "Failed to create server: %s\n", avahi_strerror(error));
727
620
returncode = EXIT_FAILURE;
731
624
/* Create the service browser */
732
sb = avahi_s_service_browser_new(server, (AvahiIfIndex)if_index,
734
"_mandos._tcp", NULL, 0,
735
browse_callback, server);
737
fprintf(stderr, "Failed to create service browser: %s\n",
738
avahi_strerror(avahi_server_errno(server)));
625
if (!(sb = avahi_s_service_browser_new(server, if_nametoindex("eth0"), AVAHI_PROTO_INET6, "_mandos._tcp", NULL, 0, browse_callback, server))) {
626
fprintf(stderr, "Failed to create service browser: %s\n", avahi_strerror(avahi_server_errno(server)));
739
627
returncode = EXIT_FAILURE;