/mandos/trunk

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/trunk

« back to all changes in this revision

Viewing changes to plugins.d/mandos-client.c

  • Committer: teddy at bsnet
  • Date: 2010-08-10 19:08:24 UTC
  • mto: This revision was merged to the branch mainline in revision 419.
  • Revision ID: teddy@fukt.bsnet.se-20100810190824-5yquozxy4kh6py3f
* plugins.d/mandos-client.c: An empty interface name now means to
                             autodetect an interface; to specify no
                             particular interface, use "none".
  (sys_class_net): New global variable for the "/sys/class/net" path.
  (good_interface): New function to determine the suitability of an
                    interface.  Used by a scandir() call in main().
  (main): Changed default value for "interface" to the empty string.
          Moved "connect_to" to be a global variable.  Only take down
          and up interface if its name is not "none".
* plugins.d/mandos-client.xml (OPTIONS): Update documentation for the
                                         "--interface" option.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* $Id$ */
2
 
 
3
 
/* PLEASE NOTE *
4
 
 * This file demonstrates how to use Avahi's core API, this is
5
 
 * the embeddable mDNS stack for embedded applications.
 
1
/*  -*- coding: utf-8 -*- */
 
2
/*
 
3
 * Mandos-client - get and decrypt data from a Mandos server
6
4
 *
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
10
 
 * 
11
 
 * I repeat, you probably do *not* want to use this example.
 
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".
 
10
 * 
 
11
 * Everything else is
 
12
 * Copyright © 2008,2009 Teddy Hogeborn
 
13
 * Copyright © 2008,2009 Björn Påhlsson
 
14
 * 
 
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.
 
19
 * 
 
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.
 
24
 * 
 
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/>.
 
28
 * 
 
29
 * Contact the authors at <mandos@fukt.bsnet.se>.
12
30
 */
13
31
 
14
 
/***
15
 
  This file is part of avahi.
16
 
 
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.
21
 
 
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.
26
 
 
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
30
 
  USA.
31
 
***/
32
 
 
 
32
/* Needed by GPGME, specifically gpgme_data_seek() */
 
33
#ifndef _LARGEFILE_SOURCE
33
34
#define _LARGEFILE_SOURCE
 
35
#endif
 
36
#ifndef _FILE_OFFSET_BITS
34
37
#define _FILE_OFFSET_BITS 64
35
 
 
36
 
#include <stdio.h>
37
 
#include <assert.h>
38
 
#include <stdlib.h>
39
 
#include <time.h>
40
 
#include <net/if.h>             /* if_nametoindex */
41
 
 
 
38
#endif
 
39
 
 
40
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY(), asprintf() */
 
41
 
 
42
#include <stdio.h>              /* fprintf(), stderr, fwrite(),
 
43
                                   stdout, ferror(), remove() */
 
44
#include <stdint.h>             /* uint16_t, uint32_t */
 
45
#include <stddef.h>             /* NULL, size_t, ssize_t */
 
46
#include <stdlib.h>             /* free(), EXIT_SUCCESS, EXIT_FAILURE,
 
47
                                   srand(), strtof(), abort() */
 
48
#include <stdbool.h>            /* bool, false, true */
 
49
#include <string.h>             /* memset(), strcmp(), strlen(),
 
50
                                   strerror(), asprintf(), strcpy() */
 
51
#include <sys/ioctl.h>          /* ioctl */
 
52
#include <sys/types.h>          /* socket(), inet_pton(), sockaddr,
 
53
                                   sockaddr_in6, PF_INET6,
 
54
                                   SOCK_STREAM, uid_t, gid_t, open(),
 
55
                                   opendir(), DIR */
 
56
#include <sys/stat.h>           /* open() */
 
57
#include <sys/socket.h>         /* socket(), struct sockaddr_in6,
 
58
                                   inet_pton(), connect() */
 
59
#include <fcntl.h>              /* open() */
 
60
#include <dirent.h>             /* opendir(), struct dirent, readdir()
 
61
                                 */
 
62
#include <inttypes.h>           /* PRIu16, PRIdMAX, intmax_t,
 
63
                                   strtoimax() */
 
64
#include <assert.h>             /* assert() */
 
65
#include <errno.h>              /* perror(), errno */
 
66
#include <time.h>               /* nanosleep(), time() */
 
67
#include <net/if.h>             /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
 
68
                                   SIOCSIFFLAGS, if_indextoname(),
 
69
                                   if_nametoindex(), IF_NAMESIZE */
 
70
#include <netinet/in.h>         /* IN6_IS_ADDR_LINKLOCAL,
 
71
                                   INET_ADDRSTRLEN, INET6_ADDRSTRLEN
 
72
                                */
 
73
#include <unistd.h>             /* close(), SEEK_SET, off_t, write(),
 
74
                                   getuid(), getgid(), seteuid(),
 
75
                                   setgid(), pause() */
 
76
#include <arpa/inet.h>          /* inet_pton(), htons */
 
77
#include <iso646.h>             /* not, or, and */
 
78
#include <argp.h>               /* struct argp_option, error_t, struct
 
79
                                   argp_state, struct argp,
 
80
                                   argp_parse(), ARGP_KEY_ARG,
 
81
                                   ARGP_KEY_END, ARGP_ERR_UNKNOWN */
 
82
#include <signal.h>             /* sigemptyset(), sigaddset(),
 
83
                                   sigaction(), SIGTERM, sig_atomic_t,
 
84
                                   raise() */
 
85
 
 
86
#ifdef __linux__
 
87
#include <sys/klog.h>           /* klogctl() */
 
88
#endif  /* __linux__ */
 
89
 
 
90
/* Avahi */
 
91
/* All Avahi types, constants and functions
 
92
 Avahi*, avahi_*,
 
93
 AVAHI_* */
42
94
#include <avahi-core/core.h>
43
95
#include <avahi-core/lookup.h>
44
96
#include <avahi-core/log.h>
46
98
#include <avahi-common/malloc.h>
47
99
#include <avahi-common/error.h>
48
100
 
49
 
//mandos client part
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 */
54
 
 
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 */
61
 
 
62
 
// gpgme
63
 
#include <errno.h>              /* perror() */
64
 
#include <gpgme.h>
65
 
 
66
 
 
67
 
#ifndef CERT_ROOT
68
 
#define CERT_ROOT "/conf/conf.d/cryptkeyreq/"
69
 
#endif
70
 
#define CERTFILE CERT_ROOT "openpgp-client.txt"
71
 
#define KEYFILE CERT_ROOT "openpgp-client-key.txt"
 
101
/* GnuTLS */
 
102
#include <gnutls/gnutls.h>      /* All GnuTLS types, constants and
 
103
                                   functions:
 
104
                                   gnutls_*
 
105
                                   init_gnutls_session(),
 
106
                                   GNUTLS_* */
 
107
#include <gnutls/openpgp.h>
 
108
                          /* gnutls_certificate_set_openpgp_key_file(),
 
109
                                   GNUTLS_OPENPGP_FMT_BASE64 */
 
110
 
 
111
/* GPGME */
 
112
#include <gpgme.h>              /* All GPGME types, constants and
 
113
                                   functions:
 
114
                                   gpgme_*
 
115
                                   GPGME_PROTOCOL_OpenPGP,
 
116
                                   GPG_ERR_NO_* */
 
117
 
72
118
#define BUFFER_SIZE 256
73
 
#define DH_BITS 1024
74
 
 
 
119
 
 
120
#define PATHDIR "/conf/conf.d/mandos"
 
121
#define SECKEY "seckey.txt"
 
122
#define PUBKEY "pubkey.txt"
 
123
 
 
124
bool debug = false;
 
125
static const char mandos_protocol_version[] = "1";
 
126
const char *argp_program_version = "mandos-client " VERSION;
 
127
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
 
128
static const char sys_class_net[] = "/sys/class/net";
 
129
char *connect_to = NULL;
 
130
 
 
131
/* Used for passing in values through the Avahi callback functions */
75
132
typedef struct {
76
 
  gnutls_session_t session;
 
133
  AvahiSimplePoll *simple_poll;
 
134
  AvahiServer *server;
77
135
  gnutls_certificate_credentials_t cred;
 
136
  unsigned int dh_bits;
78
137
  gnutls_dh_params_t dh_params;
79
 
} encrypted_session;
80
 
 
81
 
 
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;
 
138
  const char *priority;
84
139
  gpgme_ctx_t ctx;
 
140
} mandos_context;
 
141
 
 
142
/* global context so signal handler can reach it*/
 
143
mandos_context mc = { .simple_poll = NULL, .server = NULL,
 
144
                      .dh_bits = 1024, .priority = "SECURE256"
 
145
                      ":!CTYPE-X.509:+CTYPE-OPENPGP" };
 
146
 
 
147
sig_atomic_t quit_now = 0;
 
148
int signal_received = 0;
 
149
 
 
150
/*
 
151
 * Make additional room in "buffer" for at least BUFFER_SIZE more
 
152
 * bytes. "buffer_capacity" is how much is currently allocated,
 
153
 * "buffer_length" is how much is already used.
 
154
 */
 
155
size_t incbuffer(char **buffer, size_t buffer_length,
 
156
                  size_t buffer_capacity){
 
157
  if(buffer_length + BUFFER_SIZE > buffer_capacity){
 
158
    *buffer = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
 
159
    if(buffer == NULL){
 
160
      return 0;
 
161
    }
 
162
    buffer_capacity += BUFFER_SIZE;
 
163
  }
 
164
  return buffer_capacity;
 
165
}
 
166
 
 
167
/* 
 
168
 * Initialize GPGME.
 
169
 */
 
170
static bool init_gpgme(const char *seckey,
 
171
                       const char *pubkey, const char *tempdir){
85
172
  gpgme_error_t rc;
86
 
  ssize_t ret;
87
 
  size_t new_packet_capacity = 0;
88
 
  size_t new_packet_length = 0;
89
173
  gpgme_engine_info_t engine_info;
90
 
 
 
174
  
 
175
  
 
176
  /*
 
177
   * Helper function to insert pub and seckey to the engine keyring.
 
178
   */
 
179
  bool import_key(const char *filename){
 
180
    int ret;
 
181
    int fd;
 
182
    gpgme_data_t pgp_data;
 
183
    
 
184
    fd = (int)TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
 
185
    if(fd == -1){
 
186
      perror("open");
 
187
      return false;
 
188
    }
 
189
    
 
190
    rc = gpgme_data_new_from_fd(&pgp_data, fd);
 
191
    if(rc != GPG_ERR_NO_ERROR){
 
192
      fprintf(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
 
193
              gpgme_strsource(rc), gpgme_strerror(rc));
 
194
      return false;
 
195
    }
 
196
    
 
197
    rc = gpgme_op_import(mc.ctx, pgp_data);
 
198
    if(rc != GPG_ERR_NO_ERROR){
 
199
      fprintf(stderr, "bad gpgme_op_import: %s: %s\n",
 
200
              gpgme_strsource(rc), gpgme_strerror(rc));
 
201
      return false;
 
202
    }
 
203
    
 
204
    ret = (int)TEMP_FAILURE_RETRY(close(fd));
 
205
    if(ret == -1){
 
206
      perror("close");
 
207
    }
 
208
    gpgme_data_release(pgp_data);
 
209
    return true;
 
210
  }
 
211
  
 
212
  if(debug){
 
213
    fprintf(stderr, "Initializing GPGME\n");
 
214
  }
 
215
  
91
216
  /* Init GPGME */
92
217
  gpgme_check_version(NULL);
93
 
  gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
 
218
  rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
 
219
  if(rc != GPG_ERR_NO_ERROR){
 
220
    fprintf(stderr, "bad gpgme_engine_check_version: %s: %s\n",
 
221
            gpgme_strsource(rc), gpgme_strerror(rc));
 
222
    return false;
 
223
  }
94
224
  
95
 
  /* Set GPGME home directory */
96
 
  rc = gpgme_get_engine_info (&engine_info);
97
 
  if (rc != GPG_ERR_NO_ERROR){
 
225
    /* Set GPGME home directory for the OpenPGP engine only */
 
226
  rc = gpgme_get_engine_info(&engine_info);
 
227
  if(rc != GPG_ERR_NO_ERROR){
98
228
    fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
99
229
            gpgme_strsource(rc), gpgme_strerror(rc));
100
 
    return -1;
 
230
    return false;
101
231
  }
102
232
  while(engine_info != NULL){
103
233
    if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
104
234
      gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
105
 
                            engine_info->file_name, homedir);
 
235
                            engine_info->file_name, tempdir);
106
236
      break;
107
237
    }
108
238
    engine_info = engine_info->next;
109
239
  }
110
240
  if(engine_info == NULL){
111
 
    fprintf(stderr, "Could not set home dir to %s\n", homedir);
112
 
    return -1;
113
 
  }
114
 
  
115
 
  /* Create new GPGME data buffer from packet buffer */
116
 
  rc = gpgme_data_new_from_mem(&dh_crypto, packet, packet_size, 0);
117
 
  if (rc != GPG_ERR_NO_ERROR){
 
241
    fprintf(stderr, "Could not set GPGME home dir to %s\n", tempdir);
 
242
    return false;
 
243
  }
 
244
  
 
245
  /* Create new GPGME "context" */
 
246
  rc = gpgme_new(&(mc.ctx));
 
247
  if(rc != GPG_ERR_NO_ERROR){
 
248
    fprintf(stderr, "bad gpgme_new: %s: %s\n",
 
249
            gpgme_strsource(rc), gpgme_strerror(rc));
 
250
    return false;
 
251
  }
 
252
  
 
253
  if(not import_key(pubkey) or not import_key(seckey)){
 
254
    return false;
 
255
  }
 
256
  
 
257
  return true;
 
258
}
 
259
 
 
260
/* 
 
261
 * Decrypt OpenPGP data.
 
262
 * Returns -1 on error
 
263
 */
 
264
static ssize_t pgp_packet_decrypt(const char *cryptotext,
 
265
                                  size_t crypto_size,
 
266
                                  char **plaintext){
 
267
  gpgme_data_t dh_crypto, dh_plain;
 
268
  gpgme_error_t rc;
 
269
  ssize_t ret;
 
270
  size_t plaintext_capacity = 0;
 
271
  ssize_t plaintext_length = 0;
 
272
  
 
273
  if(debug){
 
274
    fprintf(stderr, "Trying to decrypt OpenPGP data\n");
 
275
  }
 
276
  
 
277
  /* Create new GPGME data buffer from memory cryptotext */
 
278
  rc = gpgme_data_new_from_mem(&dh_crypto, cryptotext, crypto_size,
 
279
                               0);
 
280
  if(rc != GPG_ERR_NO_ERROR){
118
281
    fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
119
282
            gpgme_strsource(rc), gpgme_strerror(rc));
120
283
    return -1;
122
285
  
123
286
  /* Create new empty GPGME data buffer for the plaintext */
124
287
  rc = gpgme_data_new(&dh_plain);
125
 
  if (rc != GPG_ERR_NO_ERROR){
 
288
  if(rc != GPG_ERR_NO_ERROR){
126
289
    fprintf(stderr, "bad gpgme_data_new: %s: %s\n",
127
290
            gpgme_strsource(rc), gpgme_strerror(rc));
128
 
    return -1;
129
 
  }
130
 
  
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));
136
 
    return -1;
137
 
  }
138
 
  
139
 
  /* Decrypt data from the FILE pointer to the plaintext data buffer */
140
 
  rc = gpgme_op_decrypt(ctx, dh_crypto, dh_plain);
141
 
  if (rc != GPG_ERR_NO_ERROR){
 
291
    gpgme_data_release(dh_crypto);
 
292
    return -1;
 
293
  }
 
294
  
 
295
  /* Decrypt data from the cryptotext data buffer to the plaintext
 
296
     data buffer */
 
297
  rc = gpgme_op_decrypt(mc.ctx, dh_crypto, dh_plain);
 
298
  if(rc != GPG_ERR_NO_ERROR){
142
299
    fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
143
300
            gpgme_strsource(rc), gpgme_strerror(rc));
144
 
    return -1;
 
301
    plaintext_length = -1;
 
302
    if(debug){
 
303
      gpgme_decrypt_result_t result;
 
304
      result = gpgme_op_decrypt_result(mc.ctx);
 
305
      if(result == NULL){
 
306
        fprintf(stderr, "gpgme_op_decrypt_result failed\n");
 
307
      } else {
 
308
        fprintf(stderr, "Unsupported algorithm: %s\n",
 
309
                result->unsupported_algorithm);
 
310
        fprintf(stderr, "Wrong key usage: %u\n",
 
311
                result->wrong_key_usage);
 
312
        if(result->file_name != NULL){
 
313
          fprintf(stderr, "File name: %s\n", result->file_name);
 
314
        }
 
315
        gpgme_recipient_t recipient;
 
316
        recipient = result->recipients;
 
317
        while(recipient != NULL){
 
318
          fprintf(stderr, "Public key algorithm: %s\n",
 
319
                  gpgme_pubkey_algo_name(recipient->pubkey_algo));
 
320
          fprintf(stderr, "Key ID: %s\n", recipient->keyid);
 
321
          fprintf(stderr, "Secret key available: %s\n",
 
322
                  recipient->status == GPG_ERR_NO_SECKEY
 
323
                  ? "No" : "Yes");
 
324
          recipient = recipient->next;
 
325
        }
 
326
      }
 
327
    }
 
328
    goto decrypt_end;
145
329
  }
146
330
  
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); */
153
 
/*   } */
154
 
/*   gpgme_recipient_t recipient; */
155
 
/*   recipient = result->recipients; */
156
 
/*   if(recipient){ */
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; */
164
 
/*     } */
165
 
/*   } */
166
 
 
167
 
  /* Delete the GPGME FILE pointer cryptotext data buffer */
168
 
  gpgme_data_release(dh_crypto);
 
331
  if(debug){
 
332
    fprintf(stderr, "Decryption of OpenPGP data succeeded\n");
 
333
  }
169
334
  
170
335
  /* Seek back to the beginning of the GPGME plaintext data buffer */
171
 
  gpgme_data_seek(dh_plain, 0, SEEK_SET);
172
 
 
173
 
  *new_packet = 0;
 
336
  if(gpgme_data_seek(dh_plain, (off_t)0, SEEK_SET) == -1){
 
337
    perror("gpgme_data_seek");
 
338
    plaintext_length = -1;
 
339
    goto decrypt_end;
 
340
  }
 
341
  
 
342
  *plaintext = NULL;
174
343
  while(true){
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){
178
 
        perror("realloc");
179
 
        return -1;
180
 
      }
181
 
      new_packet_capacity += BUFFER_SIZE;
 
344
    plaintext_capacity = incbuffer(plaintext,
 
345
                                      (size_t)plaintext_length,
 
346
                                      plaintext_capacity);
 
347
    if(plaintext_capacity == 0){
 
348
        perror("incbuffer");
 
349
        plaintext_length = -1;
 
350
        goto decrypt_end;
182
351
    }
183
352
    
184
 
    ret = gpgme_data_read(dh_plain, *new_packet + new_packet_length, BUFFER_SIZE);
 
353
    ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
 
354
                          BUFFER_SIZE);
185
355
    /* Print the data, if any */
186
 
    if (ret == 0){
187
 
      /* If password is empty, then a incorrect error will be printed */
 
356
    if(ret == 0){
 
357
      /* EOF */
188
358
      break;
189
359
    }
190
360
    if(ret < 0){
191
361
      perror("gpgme_data_read");
192
 
      return -1;
193
 
    }
194
 
    new_packet_length += ret;
195
 
  }
196
 
 
197
 
   /* Delete the GPGME plaintext data buffer */
 
362
      plaintext_length = -1;
 
363
      goto decrypt_end;
 
364
    }
 
365
    plaintext_length += ret;
 
366
  }
 
367
  
 
368
  if(debug){
 
369
    fprintf(stderr, "Decrypted password is: ");
 
370
    for(ssize_t i = 0; i < plaintext_length; i++){
 
371
      fprintf(stderr, "%02hhX ", (*plaintext)[i]);
 
372
    }
 
373
    fprintf(stderr, "\n");
 
374
  }
 
375
  
 
376
 decrypt_end:
 
377
  
 
378
  /* Delete the GPGME cryptotext data buffer */
 
379
  gpgme_data_release(dh_crypto);
 
380
  
 
381
  /* Delete the GPGME plaintext data buffer */
198
382
  gpgme_data_release(dh_plain);
199
 
  return new_packet_length;
 
383
  return plaintext_length;
200
384
}
201
385
 
202
 
static const char * safer_gnutls_strerror (int value) {
203
 
  const char *ret = gnutls_strerror (value);
204
 
  if (ret == NULL)
 
386
static const char * safer_gnutls_strerror(int value){
 
387
  const char *ret = gnutls_strerror(value); /* Spurious warning from
 
388
                                               -Wunreachable-code */
 
389
  if(ret == NULL)
205
390
    ret = "(unknown)";
206
391
  return ret;
207
392
}
208
393
 
209
 
void debuggnutls(int level, const char* string){
210
 
  fprintf(stderr, "%s", string);
 
394
/* GnuTLS log function callback */
 
395
static void debuggnutls(__attribute__((unused)) int level,
 
396
                        const char* string){
 
397
  fprintf(stderr, "GnuTLS: %s", string);
211
398
}
212
399
 
213
 
int initgnutls(encrypted_session *es){
214
 
  const char *err;
 
400
static int init_gnutls_global(const char *pubkeyfilename,
 
401
                              const char *seckeyfilename){
215
402
  int ret;
216
403
  
217
 
  if ((ret = gnutls_global_init ())
218
 
      != GNUTLS_E_SUCCESS) {
219
 
    fprintf (stderr, "global_init: %s\n", safer_gnutls_strerror(ret));
220
 
    return -1;
221
 
  }
222
 
 
223
 
  /* Uncomment to enable full debuggin on the gnutls library */
224
 
  /*   gnutls_global_set_log_level(11); */
225
 
  /*   gnutls_global_set_log_function(debuggnutls); */
226
 
 
227
 
 
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));
232
 
    return -1;
233
 
  }
234
 
 
 
404
  if(debug){
 
405
    fprintf(stderr, "Initializing GnuTLS\n");
 
406
  }
 
407
  
 
408
  ret = gnutls_global_init();
 
409
  if(ret != GNUTLS_E_SUCCESS){
 
410
    fprintf(stderr, "GnuTLS global_init: %s\n",
 
411
            safer_gnutls_strerror(ret));
 
412
    return -1;
 
413
  }
 
414
  
 
415
  if(debug){
 
416
    /* "Use a log level over 10 to enable all debugging options."
 
417
     * - GnuTLS manual
 
418
     */
 
419
    gnutls_global_set_log_level(11);
 
420
    gnutls_global_set_log_function(debuggnutls);
 
421
  }
 
422
  
 
423
  /* OpenPGP credentials */
 
424
  gnutls_certificate_allocate_credentials(&mc.cred);
 
425
  if(ret != GNUTLS_E_SUCCESS){
 
426
    fprintf(stderr, "GnuTLS memory error: %s\n", /* Spurious warning
 
427
                                                    from
 
428
                                                    -Wunreachable-code
 
429
                                                 */
 
430
            safer_gnutls_strerror(ret));
 
431
    gnutls_global_deinit();
 
432
    return -1;
 
433
  }
 
434
  
 
435
  if(debug){
 
436
    fprintf(stderr, "Attempting to use OpenPGP public key %s and"
 
437
            " secret key %s as GnuTLS credentials\n", pubkeyfilename,
 
438
            seckeyfilename);
 
439
  }
 
440
  
235
441
  ret = gnutls_certificate_set_openpgp_key_file
236
 
    (es->cred, CERTFILE, KEYFILE, GNUTLS_OPENPGP_FMT_BASE64);
237
 
  if (ret != GNUTLS_E_SUCCESS) {
238
 
    fprintf
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));
243
 
    return -1;
244
 
  }
245
 
 
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));
251
 
    return -1;
252
 
  }
253
 
 
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));
258
 
    return -1;
259
 
  }
260
 
 
261
 
  gnutls_certificate_set_dh_params (es->cred, es->dh_params);
262
 
 
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));
268
 
  }
269
 
 
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));
275
 
    return -1;
276
 
  }
277
 
 
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));
283
 
    return -1;
284
 
  }
285
 
 
 
442
    (mc.cred, pubkeyfilename, seckeyfilename,
 
443
     GNUTLS_OPENPGP_FMT_BASE64);
 
444
  if(ret != GNUTLS_E_SUCCESS){
 
445
    fprintf(stderr,
 
446
            "Error[%d] while reading the OpenPGP key pair ('%s',"
 
447
            " '%s')\n", ret, pubkeyfilename, seckeyfilename);
 
448
    fprintf(stderr, "The GnuTLS error is: %s\n",
 
449
            safer_gnutls_strerror(ret));
 
450
    goto globalfail;
 
451
  }
 
452
  
 
453
  /* GnuTLS server initialization */
 
454
  ret = gnutls_dh_params_init(&mc.dh_params);
 
455
  if(ret != GNUTLS_E_SUCCESS){
 
456
    fprintf(stderr, "Error in GnuTLS DH parameter initialization:"
 
457
            " %s\n", safer_gnutls_strerror(ret));
 
458
    goto globalfail;
 
459
  }
 
460
  ret = gnutls_dh_params_generate2(mc.dh_params, mc.dh_bits);
 
461
  if(ret != GNUTLS_E_SUCCESS){
 
462
    fprintf(stderr, "Error in GnuTLS prime generation: %s\n",
 
463
            safer_gnutls_strerror(ret));
 
464
    goto globalfail;
 
465
  }
 
466
  
 
467
  gnutls_certificate_set_dh_params(mc.cred, mc.dh_params);
 
468
  
 
469
  return 0;
 
470
  
 
471
 globalfail:
 
472
  
 
473
  gnutls_certificate_free_credentials(mc.cred);
 
474
  gnutls_global_deinit();
 
475
  gnutls_dh_params_deinit(mc.dh_params);
 
476
  return -1;
 
477
}
 
478
 
 
479
static int init_gnutls_session(gnutls_session_t *session){
 
480
  int ret;
 
481
  /* GnuTLS session creation */
 
482
  do {
 
483
    ret = gnutls_init(session, GNUTLS_SERVER);
 
484
    if(quit_now){
 
485
      return -1;
 
486
    }
 
487
  } while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
 
488
  if(ret != GNUTLS_E_SUCCESS){
 
489
    fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
 
490
            safer_gnutls_strerror(ret));
 
491
  }
 
492
  
 
493
  {
 
494
    const char *err;
 
495
    do {
 
496
      ret = gnutls_priority_set_direct(*session, mc.priority, &err);
 
497
      if(quit_now){
 
498
        gnutls_deinit(*session);
 
499
        return -1;
 
500
      }
 
501
    } while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
 
502
    if(ret != GNUTLS_E_SUCCESS){
 
503
      fprintf(stderr, "Syntax error at: %s\n", err);
 
504
      fprintf(stderr, "GnuTLS error: %s\n",
 
505
              safer_gnutls_strerror(ret));
 
506
      gnutls_deinit(*session);
 
507
      return -1;
 
508
    }
 
509
  }
 
510
  
 
511
  do {
 
512
    ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
 
513
                                 mc.cred);
 
514
    if(quit_now){
 
515
      gnutls_deinit(*session);
 
516
      return -1;
 
517
    }
 
518
  } while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
 
519
  if(ret != GNUTLS_E_SUCCESS){
 
520
    fprintf(stderr, "Error setting GnuTLS credentials: %s\n",
 
521
            safer_gnutls_strerror(ret));
 
522
    gnutls_deinit(*session);
 
523
    return -1;
 
524
  }
 
525
  
286
526
  /* ignore client certificate if any. */
287
 
  gnutls_certificate_server_set_request (es->session, GNUTLS_CERT_IGNORE);
 
527
  gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE);
288
528
  
289
 
  gnutls_dh_set_prime_bits (es->session, DH_BITS);
 
529
  gnutls_dh_set_prime_bits(*session, mc.dh_bits);
290
530
  
291
531
  return 0;
292
532
}
293
533
 
294
 
void empty_log(AvahiLogLevel level, const char *txt){}
 
534
/* Avahi log function callback */
 
535
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
 
536
                      __attribute__((unused)) const char *txt){}
295
537
 
296
 
int start_mandos_communcation(char *ip, uint16_t port){
297
 
  int ret, tcp_sd;
298
 
  struct sockaddr_in6 to;
299
 
  struct in6_addr ip_addr;
300
 
  encrypted_session es;
 
538
/* Called when a Mandos server is found */
 
539
static int start_mandos_communication(const char *ip, uint16_t port,
 
540
                                      AvahiIfIndex if_index,
 
541
                                      int af){
 
542
  int ret, tcp_sd = -1;
 
543
  ssize_t sret;
 
544
  union {
 
545
    struct sockaddr_in in;
 
546
    struct sockaddr_in6 in6;
 
547
  } to;
301
548
  char *buffer = NULL;
302
 
  char *decrypted_buffer;
 
549
  char *decrypted_buffer = NULL;
303
550
  size_t buffer_length = 0;
304
551
  size_t buffer_capacity = 0;
305
 
  ssize_t decrypted_buffer_size;
306
 
  int retval = 0;
307
 
 
308
 
  
309
 
  tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
310
 
  if(tcp_sd < 0) {
 
552
  size_t written;
 
553
  int retval = -1;
 
554
  gnutls_session_t session;
 
555
  int pf;                       /* Protocol family */
 
556
  
 
557
  if(quit_now){
 
558
    return -1;
 
559
  }
 
560
  
 
561
  switch(af){
 
562
  case AF_INET6:
 
563
    pf = PF_INET6;
 
564
    break;
 
565
  case AF_INET:
 
566
    pf = PF_INET;
 
567
    break;
 
568
  default:
 
569
    fprintf(stderr, "Bad address family: %d\n", af);
 
570
    return -1;
 
571
  }
 
572
  
 
573
  ret = init_gnutls_session(&session);
 
574
  if(ret != 0){
 
575
    return -1;
 
576
  }
 
577
  
 
578
  if(debug){
 
579
    fprintf(stderr, "Setting up a TCP connection to %s, port %" PRIu16
 
580
            "\n", ip, port);
 
581
  }
 
582
  
 
583
  tcp_sd = socket(pf, SOCK_STREAM, 0);
 
584
  if(tcp_sd < 0){
311
585
    perror("socket");
312
 
    return -1;
313
 
  }
314
 
  
315
 
  ret = setsockopt(tcp_sd, SOL_SOCKET, SO_BINDTODEVICE, "eth0", 5);
316
 
  if(tcp_sd < 0) {
317
 
    perror("setsockopt bindtodevice");
318
 
    return -1;
319
 
  }
320
 
  
321
 
  memset(&to,0,sizeof(to));
322
 
  to.sin6_family = AF_INET6;
323
 
  ret = inet_pton(AF_INET6, ip, &ip_addr);
324
 
  if (ret < 0 ){
 
586
    goto mandos_end;
 
587
  }
 
588
  
 
589
  if(quit_now){
 
590
    goto mandos_end;
 
591
  }
 
592
  
 
593
  memset(&to, 0, sizeof(to));
 
594
  if(af == AF_INET6){
 
595
    to.in6.sin6_family = (sa_family_t)af;
 
596
    ret = inet_pton(af, ip, &to.in6.sin6_addr);
 
597
  } else {                      /* IPv4 */
 
598
    to.in.sin_family = (sa_family_t)af;
 
599
    ret = inet_pton(af, ip, &to.in.sin_addr);
 
600
  }
 
601
  if(ret < 0 ){
325
602
    perror("inet_pton");
326
 
    return -1;
327
 
  }  
 
603
    goto mandos_end;
 
604
  }
328
605
  if(ret == 0){
329
606
    fprintf(stderr, "Bad address: %s\n", ip);
330
 
    return -1;
331
 
  }
332
 
  to.sin6_port = htons(port);
333
 
  to.sin6_scope_id = if_nametoindex("eth0");
334
 
  
335
 
  ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
336
 
  if (ret < 0){
 
607
    goto mandos_end;
 
608
  }
 
609
  if(af == AF_INET6){
 
610
    to.in6.sin6_port = htons(port); /* Spurious warnings from
 
611
                                       -Wconversion and
 
612
                                       -Wunreachable-code */
 
613
    
 
614
    if(IN6_IS_ADDR_LINKLOCAL /* Spurious warnings from */
 
615
       (&to.in6.sin6_addr)){ /* -Wstrict-aliasing=2 or lower and
 
616
                              -Wunreachable-code*/
 
617
      if(if_index == AVAHI_IF_UNSPEC){
 
618
        fprintf(stderr, "An IPv6 link-local address is incomplete"
 
619
                " without a network interface\n");
 
620
        goto mandos_end;
 
621
      }
 
622
      /* Set the network interface number as scope */
 
623
      to.in6.sin6_scope_id = (uint32_t)if_index;
 
624
    }
 
625
  } else {
 
626
    to.in.sin_port = htons(port); /* Spurious warnings from
 
627
                                     -Wconversion and
 
628
                                     -Wunreachable-code */
 
629
  }
 
630
  
 
631
  if(quit_now){
 
632
    goto mandos_end;
 
633
  }
 
634
  
 
635
  if(debug){
 
636
    if(af == AF_INET6 and if_index != AVAHI_IF_UNSPEC){
 
637
      char interface[IF_NAMESIZE];
 
638
      if(if_indextoname((unsigned int)if_index, interface) == NULL){
 
639
        perror("if_indextoname");
 
640
      } else {
 
641
        fprintf(stderr, "Connection to: %s%%%s, port %" PRIu16 "\n",
 
642
                ip, interface, port);
 
643
      }
 
644
    } else {
 
645
      fprintf(stderr, "Connection to: %s, port %" PRIu16 "\n", ip,
 
646
              port);
 
647
    }
 
648
    char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ?
 
649
                 INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = "";
 
650
    const char *pcret;
 
651
    if(af == AF_INET6){
 
652
      pcret = inet_ntop(af, &(to.in6.sin6_addr), addrstr,
 
653
                        sizeof(addrstr));
 
654
    } else {
 
655
      pcret = inet_ntop(af, &(to.in.sin_addr), addrstr,
 
656
                        sizeof(addrstr));
 
657
    }
 
658
    if(pcret == NULL){
 
659
      perror("inet_ntop");
 
660
    } else {
 
661
      if(strcmp(addrstr, ip) != 0){
 
662
        fprintf(stderr, "Canonical address form: %s\n", addrstr);
 
663
      }
 
664
    }
 
665
  }
 
666
  
 
667
  if(quit_now){
 
668
    goto mandos_end;
 
669
  }
 
670
  
 
671
  if(af == AF_INET6){
 
672
    ret = connect(tcp_sd, &to.in6, sizeof(to));
 
673
  } else {
 
674
    ret = connect(tcp_sd, &to.in, sizeof(to)); /* IPv4 */
 
675
  }
 
676
  if(ret < 0){
337
677
    perror("connect");
338
 
    return -1;
339
 
  }
340
 
  
341
 
  ret = initgnutls (&es);
342
 
  if (ret != 0){
343
 
    retval = -1;
344
 
    return -1;
345
 
  }
346
 
    
347
 
  
348
 
  gnutls_transport_set_ptr (es.session, (gnutls_transport_ptr_t) tcp_sd);
349
 
 
350
 
  ret = gnutls_handshake (es.session);
351
 
  
352
 
  if (ret != GNUTLS_E_SUCCESS){
353
 
    fprintf(stderr, "\n*** Handshake failed ***\n");
354
 
    gnutls_perror (ret);
355
 
    retval = -1;
356
 
    goto exit;
357
 
  }
358
 
 
359
 
  //retrive password
 
678
    goto mandos_end;
 
679
  }
 
680
  
 
681
  if(quit_now){
 
682
    goto mandos_end;
 
683
  }
 
684
  
 
685
  const char *out = mandos_protocol_version;
 
686
  written = 0;
360
687
  while(true){
361
 
    if (buffer_length + BUFFER_SIZE > buffer_capacity){
362
 
      buffer = realloc(buffer, buffer_capacity + BUFFER_SIZE);
363
 
      if (buffer == NULL){
364
 
        perror("realloc");
365
 
        goto exit;
 
688
    size_t out_size = strlen(out);
 
689
    ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
 
690
                                   out_size - written));
 
691
    if(ret == -1){
 
692
      perror("write");
 
693
      goto mandos_end;
 
694
    }
 
695
    written += (size_t)ret;
 
696
    if(written < out_size){
 
697
      continue;
 
698
    } else {
 
699
      if(out == mandos_protocol_version){
 
700
        written = 0;
 
701
        out = "\r\n";
 
702
      } else {
 
703
        break;
366
704
      }
367
 
      buffer_capacity += BUFFER_SIZE;
368
 
    }
369
 
    
370
 
    ret = gnutls_record_recv
371
 
      (es.session, buffer+buffer_length, BUFFER_SIZE);
372
 
    if (ret == 0){
 
705
    }
 
706
  
 
707
    if(quit_now){
 
708
      goto mandos_end;
 
709
    }
 
710
  }
 
711
  
 
712
  if(debug){
 
713
    fprintf(stderr, "Establishing TLS session with %s\n", ip);
 
714
  }
 
715
  
 
716
  if(quit_now){
 
717
    goto mandos_end;
 
718
  }
 
719
  
 
720
  gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t) tcp_sd);
 
721
  
 
722
  if(quit_now){
 
723
    goto mandos_end;
 
724
  }
 
725
  
 
726
  do {
 
727
    ret = gnutls_handshake(session);
 
728
    if(quit_now){
 
729
      goto mandos_end;
 
730
    }
 
731
  } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
 
732
  
 
733
  if(ret != GNUTLS_E_SUCCESS){
 
734
    if(debug){
 
735
      fprintf(stderr, "*** GnuTLS Handshake failed ***\n");
 
736
      gnutls_perror(ret);
 
737
    }
 
738
    goto mandos_end;
 
739
  }
 
740
  
 
741
  /* Read OpenPGP packet that contains the wanted password */
 
742
  
 
743
  if(debug){
 
744
    fprintf(stderr, "Retrieving OpenPGP encrypted password from %s\n",
 
745
            ip);
 
746
  }
 
747
  
 
748
  while(true){
 
749
    
 
750
    if(quit_now){
 
751
      goto mandos_end;
 
752
    }
 
753
    
 
754
    buffer_capacity = incbuffer(&buffer, buffer_length,
 
755
                                   buffer_capacity);
 
756
    if(buffer_capacity == 0){
 
757
      perror("incbuffer");
 
758
      goto mandos_end;
 
759
    }
 
760
    
 
761
    if(quit_now){
 
762
      goto mandos_end;
 
763
    }
 
764
    
 
765
    sret = gnutls_record_recv(session, buffer+buffer_length,
 
766
                              BUFFER_SIZE);
 
767
    if(sret == 0){
373
768
      break;
374
769
    }
375
 
    if (ret < 0){
376
 
      switch(ret){
 
770
    if(sret < 0){
 
771
      switch(sret){
377
772
      case GNUTLS_E_INTERRUPTED:
378
773
      case GNUTLS_E_AGAIN:
379
774
        break;
380
775
      case GNUTLS_E_REHANDSHAKE:
381
 
        ret = gnutls_handshake (es.session);
382
 
        if (ret < 0){
383
 
          fprintf(stderr, "\n*** Handshake failed ***\n");
384
 
          gnutls_perror (ret);
385
 
          retval = -1;
386
 
          goto exit;
 
776
        do {
 
777
          ret = gnutls_handshake(session);
 
778
          
 
779
          if(quit_now){
 
780
            goto mandos_end;
 
781
          }
 
782
        } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
 
783
        if(ret < 0){
 
784
          fprintf(stderr, "*** GnuTLS Re-handshake failed ***\n");
 
785
          gnutls_perror(ret);
 
786
          goto mandos_end;
387
787
        }
388
788
        break;
389
789
      default:
390
 
        fprintf(stderr, "Unknown error while reading data from encrypted session with mandos server\n");
391
 
        retval = -1;
392
 
        gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
393
 
        goto exit;
394
 
      }
395
 
    } else {
396
 
      buffer_length += ret;
397
 
    }
398
 
  }
399
 
 
400
 
  if (buffer_length > 0){
401
 
    if ((decrypted_buffer_size = gpg_packet_decrypt(buffer, buffer_length, &decrypted_buffer, CERT_ROOT)) == 0){
402
 
      retval = -1;
403
 
    } else {
404
 
      fwrite (decrypted_buffer, 1, decrypted_buffer_size, stdout);
405
 
      free(decrypted_buffer);
406
 
    }
407
 
  }
408
 
 
 
790
        fprintf(stderr, "Unknown error while reading data from"
 
791
                " encrypted session with Mandos server\n");
 
792
        gnutls_bye(session, GNUTLS_SHUT_RDWR);
 
793
        goto mandos_end;
 
794
      }
 
795
    } else {
 
796
      buffer_length += (size_t) sret;
 
797
    }
 
798
  }
 
799
  
 
800
  if(debug){
 
801
    fprintf(stderr, "Closing TLS session\n");
 
802
  }
 
803
  
 
804
  if(quit_now){
 
805
    goto mandos_end;
 
806
  }
 
807
  
 
808
  do {
 
809
    ret = gnutls_bye(session, GNUTLS_SHUT_RDWR);
 
810
    if(quit_now){
 
811
      goto mandos_end;
 
812
    }
 
813
  } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
 
814
  
 
815
  if(buffer_length > 0){
 
816
    ssize_t decrypted_buffer_size;
 
817
    decrypted_buffer_size = pgp_packet_decrypt(buffer,
 
818
                                               buffer_length,
 
819
                                               &decrypted_buffer);
 
820
    if(decrypted_buffer_size >= 0){
 
821
      
 
822
      written = 0;
 
823
      while(written < (size_t) decrypted_buffer_size){
 
824
        if(quit_now){
 
825
          goto mandos_end;
 
826
        }
 
827
        
 
828
        ret = (int)fwrite(decrypted_buffer + written, 1,
 
829
                          (size_t)decrypted_buffer_size - written,
 
830
                          stdout);
 
831
        if(ret == 0 and ferror(stdout)){
 
832
          if(debug){
 
833
            fprintf(stderr, "Error writing encrypted data: %s\n",
 
834
                    strerror(errno));
 
835
          }
 
836
          goto mandos_end;
 
837
        }
 
838
        written += (size_t)ret;
 
839
      }
 
840
      retval = 0;
 
841
    }
 
842
  }
 
843
  
 
844
  /* Shutdown procedure */
 
845
  
 
846
 mandos_end:
 
847
  free(decrypted_buffer);
409
848
  free(buffer);
410
 
 
411
 
  //shutdown procedure
412
 
  gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
413
 
 exit:
414
 
  close(tcp_sd);
415
 
  gnutls_deinit (es.session);
416
 
  gnutls_certificate_free_credentials (es.cred);
417
 
  gnutls_global_deinit ();
 
849
  if(tcp_sd >= 0){
 
850
    ret = (int)TEMP_FAILURE_RETRY(close(tcp_sd));
 
851
  }
 
852
  if(ret == -1){
 
853
    perror("close");
 
854
  }
 
855
  gnutls_deinit(session);
 
856
  if(quit_now){
 
857
    retval = -1;
 
858
  }
418
859
  return retval;
419
860
}
420
861
 
421
 
static AvahiSimplePoll *simple_poll = NULL;
422
 
static AvahiServer *server = NULL;
423
 
 
424
 
static void resolve_callback(
425
 
    AvahiSServiceResolver *r,
426
 
    AVAHI_GCC_UNUSED AvahiIfIndex interface,
427
 
    AVAHI_GCC_UNUSED AvahiProtocol protocol,
428
 
    AvahiResolverEvent event,
429
 
    const char *name,
430
 
    const char *type,
431
 
    const char *domain,
432
 
    const char *host_name,
433
 
    const AvahiAddress *address,
434
 
    uint16_t port,
435
 
    AvahiStringList *txt,
436
 
    AvahiLookupResultFlags flags,
437
 
    AVAHI_GCC_UNUSED void* userdata) {
438
 
    
439
 
    assert(r);
440
 
 
441
 
    /* Called whenever a service has been resolved successfully or timed out */
442
 
 
443
 
    switch (event) {
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)));
446
 
            break;
447
 
 
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);
452
 
            if (ret == 0){
453
 
              exit(EXIT_SUCCESS);
454
 
            } else {
455
 
              exit(EXIT_FAILURE);
456
 
            }
457
 
        }
458
 
    }
459
 
    avahi_s_service_resolver_free(r);
460
 
}
461
 
 
462
 
static void browse_callback(
463
 
    AvahiSServiceBrowser *b,
464
 
    AvahiIfIndex interface,
465
 
    AvahiProtocol protocol,
466
 
    AvahiBrowserEvent event,
467
 
    const char *name,
468
 
    const char *type,
469
 
    const char *domain,
470
 
    AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
471
 
    void* userdata) {
472
 
    
473
 
    AvahiServer *s = userdata;
474
 
    assert(b);
475
 
 
476
 
    /* Called whenever a new services becomes available on the LAN or is removed from the LAN */
477
 
 
478
 
    switch (event) {
479
 
 
480
 
        case AVAHI_BROWSER_FAILURE:
481
 
            
482
 
            fprintf(stderr, "(Browser) %s\n", avahi_strerror(avahi_server_errno(server)));
483
 
            avahi_simple_poll_quit(simple_poll);
484
 
            return;
485
 
 
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. */
491
 
            
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)));
494
 
            
495
 
            break;
496
 
 
497
 
        case AVAHI_BROWSER_REMOVE:
498
 
            break;
499
 
 
500
 
        case AVAHI_BROWSER_ALL_FOR_NOW:
501
 
        case AVAHI_BROWSER_CACHE_EXHAUSTED:
502
 
            break;
503
 
    }
504
 
}
505
 
 
506
 
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
 
862
static void resolve_callback(AvahiSServiceResolver *r,
 
863
                             AvahiIfIndex interface,
 
864
                             AvahiProtocol proto,
 
865
                             AvahiResolverEvent event,
 
866
                             const char *name,
 
867
                             const char *type,
 
868
                             const char *domain,
 
869
                             const char *host_name,
 
870
                             const AvahiAddress *address,
 
871
                             uint16_t port,
 
872
                             AVAHI_GCC_UNUSED AvahiStringList *txt,
 
873
                             AVAHI_GCC_UNUSED AvahiLookupResultFlags
 
874
                             flags,
 
875
                             AVAHI_GCC_UNUSED void* userdata){
 
876
  assert(r);
 
877
  
 
878
  /* Called whenever a service has been resolved successfully or
 
879
     timed out */
 
880
  
 
881
  if(quit_now){
 
882
    return;
 
883
  }
 
884
  
 
885
  switch(event){
 
886
  default:
 
887
  case AVAHI_RESOLVER_FAILURE:
 
888
    fprintf(stderr, "(Avahi Resolver) Failed to resolve service '%s'"
 
889
            " of type '%s' in domain '%s': %s\n", name, type, domain,
 
890
            avahi_strerror(avahi_server_errno(mc.server)));
 
891
    break;
 
892
    
 
893
  case AVAHI_RESOLVER_FOUND:
 
894
    {
 
895
      char ip[AVAHI_ADDRESS_STR_MAX];
 
896
      avahi_address_snprint(ip, sizeof(ip), address);
 
897
      if(debug){
 
898
        fprintf(stderr, "Mandos server \"%s\" found on %s (%s, %"
 
899
                PRIdMAX ") on port %" PRIu16 "\n", name, host_name,
 
900
                ip, (intmax_t)interface, port);
 
901
      }
 
902
      int ret = start_mandos_communication(ip, port, interface,
 
903
                                           avahi_proto_to_af(proto));
 
904
      if(ret == 0){
 
905
        avahi_simple_poll_quit(mc.simple_poll);
 
906
      }
 
907
    }
 
908
  }
 
909
  avahi_s_service_resolver_free(r);
 
910
}
 
911
 
 
912
static void browse_callback(AvahiSServiceBrowser *b,
 
913
                            AvahiIfIndex interface,
 
914
                            AvahiProtocol protocol,
 
915
                            AvahiBrowserEvent event,
 
916
                            const char *name,
 
917
                            const char *type,
 
918
                            const char *domain,
 
919
                            AVAHI_GCC_UNUSED AvahiLookupResultFlags
 
920
                            flags,
 
921
                            AVAHI_GCC_UNUSED void* userdata){
 
922
  assert(b);
 
923
  
 
924
  /* Called whenever a new services becomes available on the LAN or
 
925
     is removed from the LAN */
 
926
  
 
927
  if(quit_now){
 
928
    return;
 
929
  }
 
930
  
 
931
  switch(event){
 
932
  default:
 
933
  case AVAHI_BROWSER_FAILURE:
 
934
    
 
935
    fprintf(stderr, "(Avahi browser) %s\n",
 
936
            avahi_strerror(avahi_server_errno(mc.server)));
 
937
    avahi_simple_poll_quit(mc.simple_poll);
 
938
    return;
 
939
    
 
940
  case AVAHI_BROWSER_NEW:
 
941
    /* We ignore the returned Avahi resolver object. In the callback
 
942
       function we free it. If the Avahi server is terminated before
 
943
       the callback function is called the Avahi server will free the
 
944
       resolver for us. */
 
945
    
 
946
    if(avahi_s_service_resolver_new(mc.server, interface, protocol,
 
947
                                    name, type, domain, protocol, 0,
 
948
                                    resolve_callback, NULL) == NULL)
 
949
      fprintf(stderr, "Avahi: Failed to resolve service '%s': %s\n",
 
950
              name, avahi_strerror(avahi_server_errno(mc.server)));
 
951
    break;
 
952
    
 
953
  case AVAHI_BROWSER_REMOVE:
 
954
    break;
 
955
    
 
956
  case AVAHI_BROWSER_ALL_FOR_NOW:
 
957
  case AVAHI_BROWSER_CACHE_EXHAUSTED:
 
958
    if(debug){
 
959
      fprintf(stderr, "No Mandos server found, still searching...\n");
 
960
    }
 
961
    break;
 
962
  }
 
963
}
 
964
 
 
965
/* stop main loop after sigterm has been called */
 
966
static void handle_sigterm(int sig){
 
967
  if(quit_now){
 
968
    return;
 
969
  }
 
970
  quit_now = 1;
 
971
  signal_received = sig;
 
972
  int old_errno = errno;
 
973
  if(mc.simple_poll != NULL){
 
974
    avahi_simple_poll_quit(mc.simple_poll);
 
975
  }
 
976
  errno = old_errno;
 
977
}
 
978
 
 
979
/* 
 
980
 * This function determines if a directory entry in /sys/class/net
 
981
 * corresponds to an acceptable network device.
 
982
 * (This function is passed to scandir(3) as a filter function.)
 
983
 */
 
984
int good_interface(const struct dirent *if_entry){
 
985
  ssize_t ssret;
 
986
  char *flagname = NULL;
 
987
  int ret = asprintf(&flagname, "%s/%s/flags", sys_class_net,
 
988
                     if_entry->d_name);
 
989
  if(ret < 0){
 
990
    perror("asprintf");
 
991
    return 0;
 
992
  }
 
993
  if(if_entry->d_name[0] == '.'){
 
994
    return 0;
 
995
  }
 
996
  int flags_fd = (int)TEMP_FAILURE_RETRY(open(flagname, O_RDONLY));
 
997
  if(flags_fd == -1){
 
998
    perror("open");
 
999
    return 0;
 
1000
  }
 
1001
  typedef short ifreq_flags;    /* ifreq.ifr_flags in netdevice(7) */
 
1002
  /* read line from flags_fd */
 
1003
  ssize_t to_read = (sizeof(ifreq_flags)*2)+3; /* "0x1003\n" */
 
1004
  char *flagstring = malloc((size_t)to_read);
 
1005
  if(flagstring == NULL){
 
1006
    perror("malloc");
 
1007
    close(flags_fd);
 
1008
    return 0;
 
1009
  }
 
1010
  while(to_read > 0){
 
1011
    ssret = (ssize_t)TEMP_FAILURE_RETRY(read(flags_fd, flagstring,
 
1012
                                             (size_t)to_read));
 
1013
    if(ssret == -1){
 
1014
      perror("read");
 
1015
      free(flagstring);
 
1016
      close(flags_fd);
 
1017
      return 0;
 
1018
    }
 
1019
    to_read -= ssret;
 
1020
    if(ssret == 0){
 
1021
      break;
 
1022
    }
 
1023
  }
 
1024
  close(flags_fd);
 
1025
  intmax_t tmpmax;
 
1026
  char *tmp;
 
1027
  errno = 0;
 
1028
  tmpmax = strtoimax(flagstring, &tmp, 0);
 
1029
  if(errno != 0 or tmp == flagstring or (*tmp != '\0'
 
1030
                                         and not (isspace(*tmp)))
 
1031
     or tmpmax != (ifreq_flags)tmpmax){
 
1032
    free(flagstring);
 
1033
    return 0;
 
1034
  }
 
1035
  free(flagstring);
 
1036
  ifreq_flags flags = (ifreq_flags)tmpmax;
 
1037
  /* Reject the loopback device */
 
1038
  if(flags & IFF_LOOPBACK){
 
1039
    return 0;
 
1040
  }
 
1041
  /* Accept point-to-point devices only if connect_to is specified */
 
1042
  if(connect_to != NULL and (flags & IFF_POINTOPOINT)){
 
1043
    return 1;
 
1044
  }
 
1045
  /* Otherwise, reject non-broadcast-capable devices */
 
1046
  if(not (flags & IFF_BROADCAST)){
 
1047
    return 0;
 
1048
  }
 
1049
  /* Accept this device */
 
1050
  return 1;
 
1051
}
 
1052
 
 
1053
int main(int argc, char *argv[]){
 
1054
  AvahiSServiceBrowser *sb = NULL;
 
1055
  int error;
 
1056
  int ret;
 
1057
  intmax_t tmpmax;
 
1058
  char *tmp;
 
1059
  int exitcode = EXIT_SUCCESS;
 
1060
  const char *interface = "";
 
1061
  struct ifreq network;
 
1062
  int sd = -1;
 
1063
  bool take_down_interface = false;
 
1064
  uid_t uid;
 
1065
  gid_t gid;
 
1066
  char tempdir[] = "/tmp/mandosXXXXXX";
 
1067
  bool tempdir_created = false;
 
1068
  AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
 
1069
  const char *seckey = PATHDIR "/" SECKEY;
 
1070
  const char *pubkey = PATHDIR "/" PUBKEY;
 
1071
  
 
1072
  bool gnutls_initialized = false;
 
1073
  bool gpgme_initialized = false;
 
1074
  float delay = 2.5f;
 
1075
  
 
1076
  struct sigaction old_sigterm_action = { .sa_handler = SIG_DFL };
 
1077
  struct sigaction sigterm_action = { .sa_handler = handle_sigterm };
 
1078
  
 
1079
  uid = getuid();
 
1080
  gid = getgid();
 
1081
  
 
1082
  /* Lower any group privileges we might have, just to be safe */
 
1083
  errno = 0;
 
1084
  ret = setgid(gid);
 
1085
  if(ret == -1){
 
1086
    perror("setgid");
 
1087
  }
 
1088
  
 
1089
  /* Lower user privileges (temporarily) */
 
1090
  errno = 0;
 
1091
  ret = seteuid(uid);
 
1092
  if(ret == -1){
 
1093
    perror("seteuid");
 
1094
  }
 
1095
  
 
1096
  if(quit_now){
 
1097
    goto end;
 
1098
  }
 
1099
  
 
1100
  {
 
1101
    struct argp_option options[] = {
 
1102
      { .name = "debug", .key = 128,
 
1103
        .doc = "Debug mode", .group = 3 },
 
1104
      { .name = "connect", .key = 'c',
 
1105
        .arg = "ADDRESS:PORT",
 
1106
        .doc = "Connect directly to a specific Mandos server",
 
1107
        .group = 1 },
 
1108
      { .name = "interface", .key = 'i',
 
1109
        .arg = "NAME",
 
1110
        .doc = "Network interface that will be used to search for"
 
1111
        " Mandos servers",
 
1112
        .group = 1 },
 
1113
      { .name = "seckey", .key = 's',
 
1114
        .arg = "FILE",
 
1115
        .doc = "OpenPGP secret key file base name",
 
1116
        .group = 1 },
 
1117
      { .name = "pubkey", .key = 'p',
 
1118
        .arg = "FILE",
 
1119
        .doc = "OpenPGP public key file base name",
 
1120
        .group = 2 },
 
1121
      { .name = "dh-bits", .key = 129,
 
1122
        .arg = "BITS",
 
1123
        .doc = "Bit length of the prime number used in the"
 
1124
        " Diffie-Hellman key exchange",
 
1125
        .group = 2 },
 
1126
      { .name = "priority", .key = 130,
 
1127
        .arg = "STRING",
 
1128
        .doc = "GnuTLS priority string for the TLS handshake",
 
1129
        .group = 1 },
 
1130
      { .name = "delay", .key = 131,
 
1131
        .arg = "SECONDS",
 
1132
        .doc = "Maximum delay to wait for interface startup",
 
1133
        .group = 2 },
 
1134
      { .name = NULL }
 
1135
    };
 
1136
    
 
1137
    error_t parse_opt(int key, char *arg,
 
1138
                      struct argp_state *state){
 
1139
      switch(key){
 
1140
      case 128:                 /* --debug */
 
1141
        debug = true;
 
1142
        break;
 
1143
      case 'c':                 /* --connect */
 
1144
        connect_to = arg;
 
1145
        break;
 
1146
      case 'i':                 /* --interface */
 
1147
        interface = arg;
 
1148
        break;
 
1149
      case 's':                 /* --seckey */
 
1150
        seckey = arg;
 
1151
        break;
 
1152
      case 'p':                 /* --pubkey */
 
1153
        pubkey = arg;
 
1154
        break;
 
1155
      case 129:                 /* --dh-bits */
 
1156
        errno = 0;
 
1157
        tmpmax = strtoimax(arg, &tmp, 10);
 
1158
        if(errno != 0 or tmp == arg or *tmp != '\0'
 
1159
           or tmpmax != (typeof(mc.dh_bits))tmpmax){
 
1160
          fprintf(stderr, "Bad number of DH bits\n");
 
1161
          exit(EXIT_FAILURE);
 
1162
        }
 
1163
        mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
 
1164
        break;
 
1165
      case 130:                 /* --priority */
 
1166
        mc.priority = arg;
 
1167
        break;
 
1168
      case 131:                 /* --delay */
 
1169
        errno = 0;
 
1170
        delay = strtof(arg, &tmp);
 
1171
        if(errno != 0 or tmp == arg or *tmp != '\0'){
 
1172
          fprintf(stderr, "Bad delay\n");
 
1173
          exit(EXIT_FAILURE);
 
1174
        }
 
1175
        break;
 
1176
      case ARGP_KEY_ARG:
 
1177
        argp_usage(state);
 
1178
      case ARGP_KEY_END:
 
1179
        break;
 
1180
      default:
 
1181
        return ARGP_ERR_UNKNOWN;
 
1182
      }
 
1183
      return 0;
 
1184
    }
 
1185
    
 
1186
    struct argp argp = { .options = options, .parser = parse_opt,
 
1187
                         .args_doc = "",
 
1188
                         .doc = "Mandos client -- Get and decrypt"
 
1189
                         " passwords from a Mandos server" };
 
1190
    ret = argp_parse(&argp, argc, argv, 0, 0, NULL);
 
1191
    if(ret == ARGP_ERR_UNKNOWN){
 
1192
      fprintf(stderr, "Unknown error while parsing arguments\n");
 
1193
      exitcode = EXIT_FAILURE;
 
1194
      goto end;
 
1195
    }
 
1196
  }
 
1197
  
 
1198
  if(not debug){
 
1199
    avahi_set_log_function(empty_log);
 
1200
  }
 
1201
 
 
1202
  if(interface[0] == '\0'){
 
1203
    struct dirent **direntries;
 
1204
    ret = scandir(sys_class_net, &direntries, good_interface,
 
1205
                  alphasort);
 
1206
    if(ret >= 1){
 
1207
      /* Pick the first good interface */
 
1208
      interface = strdup(direntries[0]->d_name);
 
1209
      if(interface == NULL){
 
1210
        perror("malloc");
 
1211
        free(direntries);
 
1212
        exitcode = EXIT_FAILURE;
 
1213
        goto end;
 
1214
      }
 
1215
      free(direntries);
 
1216
    } else {
 
1217
      free(direntries);
 
1218
      fprintf(stderr, "Could not find a network interface\n");
 
1219
      exitcode = EXIT_FAILURE;
 
1220
      goto end;
 
1221
    }
 
1222
  }
 
1223
  
 
1224
  /* Initialize Avahi early so avahi_simple_poll_quit() can be called
 
1225
     from the signal handler */
 
1226
  /* Initialize the pseudo-RNG for Avahi */
 
1227
  srand((unsigned int) time(NULL));
 
1228
  mc.simple_poll = avahi_simple_poll_new();
 
1229
  if(mc.simple_poll == NULL){
 
1230
    fprintf(stderr, "Avahi: Failed to create simple poll object.\n");
 
1231
    exitcode = EXIT_FAILURE;
 
1232
    goto end;
 
1233
  }
 
1234
  
 
1235
  sigemptyset(&sigterm_action.sa_mask);
 
1236
  ret = sigaddset(&sigterm_action.sa_mask, SIGINT);
 
1237
  if(ret == -1){
 
1238
    perror("sigaddset");
 
1239
    exitcode = EXIT_FAILURE;
 
1240
    goto end;
 
1241
  }
 
1242
  ret = sigaddset(&sigterm_action.sa_mask, SIGHUP);
 
1243
  if(ret == -1){
 
1244
    perror("sigaddset");
 
1245
    exitcode = EXIT_FAILURE;
 
1246
    goto end;
 
1247
  }
 
1248
  ret = sigaddset(&sigterm_action.sa_mask, SIGTERM);
 
1249
  if(ret == -1){
 
1250
    perror("sigaddset");
 
1251
    exitcode = EXIT_FAILURE;
 
1252
    goto end;
 
1253
  }
 
1254
  /* Need to check if the handler is SIG_IGN before handling:
 
1255
     | [[info:libc:Initial Signal Actions]] |
 
1256
     | [[info:libc:Basic Signal Handling]]  |
 
1257
  */
 
1258
  ret = sigaction(SIGINT, NULL, &old_sigterm_action);
 
1259
  if(ret == -1){
 
1260
    perror("sigaction");
 
1261
    return EXIT_FAILURE;
 
1262
  }
 
1263
  if(old_sigterm_action.sa_handler != SIG_IGN){
 
1264
    ret = sigaction(SIGINT, &sigterm_action, NULL);
 
1265
    if(ret == -1){
 
1266
      perror("sigaction");
 
1267
      exitcode = EXIT_FAILURE;
 
1268
      goto end;
 
1269
    }
 
1270
  }
 
1271
  ret = sigaction(SIGHUP, NULL, &old_sigterm_action);
 
1272
  if(ret == -1){
 
1273
    perror("sigaction");
 
1274
    return EXIT_FAILURE;
 
1275
  }
 
1276
  if(old_sigterm_action.sa_handler != SIG_IGN){
 
1277
    ret = sigaction(SIGHUP, &sigterm_action, NULL);
 
1278
    if(ret == -1){
 
1279
      perror("sigaction");
 
1280
      exitcode = EXIT_FAILURE;
 
1281
      goto end;
 
1282
    }
 
1283
  }
 
1284
  ret = sigaction(SIGTERM, NULL, &old_sigterm_action);
 
1285
  if(ret == -1){
 
1286
    perror("sigaction");
 
1287
    return EXIT_FAILURE;
 
1288
  }
 
1289
  if(old_sigterm_action.sa_handler != SIG_IGN){
 
1290
    ret = sigaction(SIGTERM, &sigterm_action, NULL);
 
1291
    if(ret == -1){
 
1292
      perror("sigaction");
 
1293
      exitcode = EXIT_FAILURE;
 
1294
      goto end;
 
1295
    }
 
1296
  }
 
1297
  
 
1298
  /* If the interface is down, bring it up */
 
1299
  if(strcmp(interface, "none") != 0){
 
1300
    if_index = (AvahiIfIndex) if_nametoindex(interface);
 
1301
    if(if_index == 0){
 
1302
      fprintf(stderr, "No such interface: \"%s\"\n", interface);
 
1303
      exitcode = EXIT_FAILURE;
 
1304
      goto end;
 
1305
    }
 
1306
    
 
1307
    if(quit_now){
 
1308
      goto end;
 
1309
    }
 
1310
    
 
1311
    /* Re-raise priviliges */
 
1312
    errno = 0;
 
1313
    ret = seteuid(0);
 
1314
    if(ret == -1){
 
1315
      perror("seteuid");
 
1316
    }
 
1317
    
 
1318
#ifdef __linux__
 
1319
    /* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
 
1320
       messages to mess up the prompt */
 
1321
    ret = klogctl(8, NULL, 5);
 
1322
    bool restore_loglevel = true;
 
1323
    if(ret == -1){
 
1324
      restore_loglevel = false;
 
1325
      perror("klogctl");
 
1326
    }
 
1327
#endif  /* __linux__ */
 
1328
    
 
1329
    sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
 
1330
    if(sd < 0){
 
1331
      perror("socket");
 
1332
      exitcode = EXIT_FAILURE;
 
1333
#ifdef __linux__
 
1334
      if(restore_loglevel){
 
1335
        ret = klogctl(7, NULL, 0);
 
1336
        if(ret == -1){
 
1337
          perror("klogctl");
 
1338
        }
 
1339
      }
 
1340
#endif  /* __linux__ */
 
1341
      /* Lower privileges */
 
1342
      errno = 0;
 
1343
      ret = seteuid(uid);
 
1344
      if(ret == -1){
 
1345
        perror("seteuid");
 
1346
      }
 
1347
      goto end;
 
1348
    }
 
1349
    strcpy(network.ifr_name, interface);
 
1350
    ret = ioctl(sd, SIOCGIFFLAGS, &network);
 
1351
    if(ret == -1){
 
1352
      perror("ioctl SIOCGIFFLAGS");
 
1353
#ifdef __linux__
 
1354
      if(restore_loglevel){
 
1355
        ret = klogctl(7, NULL, 0);
 
1356
        if(ret == -1){
 
1357
          perror("klogctl");
 
1358
        }
 
1359
      }
 
1360
#endif  /* __linux__ */
 
1361
      exitcode = EXIT_FAILURE;
 
1362
      /* Lower privileges */
 
1363
      errno = 0;
 
1364
      ret = seteuid(uid);
 
1365
      if(ret == -1){
 
1366
        perror("seteuid");
 
1367
      }
 
1368
      goto end;
 
1369
    }
 
1370
    if((network.ifr_flags & IFF_UP) == 0){
 
1371
      network.ifr_flags |= IFF_UP;
 
1372
      take_down_interface = true;
 
1373
      ret = ioctl(sd, SIOCSIFFLAGS, &network);
 
1374
      if(ret == -1){
 
1375
        take_down_interface = false;
 
1376
        perror("ioctl SIOCSIFFLAGS");
 
1377
        exitcode = EXIT_FAILURE;
 
1378
#ifdef __linux__
 
1379
        if(restore_loglevel){
 
1380
          ret = klogctl(7, NULL, 0);
 
1381
          if(ret == -1){
 
1382
            perror("klogctl");
 
1383
          }
 
1384
        }
 
1385
#endif  /* __linux__ */
 
1386
        /* Lower privileges */
 
1387
        errno = 0;
 
1388
        ret = seteuid(uid);
 
1389
        if(ret == -1){
 
1390
          perror("seteuid");
 
1391
        }
 
1392
        goto end;
 
1393
      }
 
1394
    }
 
1395
    /* sleep checking until interface is running */
 
1396
    for(int i=0; i < delay * 4; i++){
 
1397
      ret = ioctl(sd, SIOCGIFFLAGS, &network);
 
1398
      if(ret == -1){
 
1399
        perror("ioctl SIOCGIFFLAGS");
 
1400
      } else if(network.ifr_flags & IFF_RUNNING){
 
1401
        break;
 
1402
      }
 
1403
      struct timespec sleeptime = { .tv_nsec = 250000000 };
 
1404
      ret = nanosleep(&sleeptime, NULL);
 
1405
      if(ret == -1 and errno != EINTR){
 
1406
        perror("nanosleep");
 
1407
      }
 
1408
    }
 
1409
    if(not take_down_interface){
 
1410
      /* We won't need the socket anymore */
 
1411
      ret = (int)TEMP_FAILURE_RETRY(close(sd));
 
1412
      if(ret == -1){
 
1413
        perror("close");
 
1414
      }
 
1415
    }
 
1416
#ifdef __linux__
 
1417
    if(restore_loglevel){
 
1418
      /* Restores kernel loglevel to default */
 
1419
      ret = klogctl(7, NULL, 0);
 
1420
      if(ret == -1){
 
1421
        perror("klogctl");
 
1422
      }
 
1423
    }
 
1424
#endif  /* __linux__ */
 
1425
    /* Lower privileges */
 
1426
    errno = 0;
 
1427
    if(take_down_interface){
 
1428
      /* Lower privileges */
 
1429
      ret = seteuid(uid);
 
1430
      if(ret == -1){
 
1431
        perror("seteuid");
 
1432
      }
 
1433
    } else {
 
1434
      /* Lower privileges permanently */
 
1435
      ret = setuid(uid);
 
1436
      if(ret == -1){
 
1437
        perror("setuid");
 
1438
      }
 
1439
    }
 
1440
  }
 
1441
  
 
1442
  if(quit_now){
 
1443
    goto end;
 
1444
  }
 
1445
  
 
1446
  ret = init_gnutls_global(pubkey, seckey);
 
1447
  if(ret == -1){
 
1448
    fprintf(stderr, "init_gnutls_global failed\n");
 
1449
    exitcode = EXIT_FAILURE;
 
1450
    goto end;
 
1451
  } else {
 
1452
    gnutls_initialized = true;
 
1453
  }
 
1454
  
 
1455
  if(quit_now){
 
1456
    goto end;
 
1457
  }
 
1458
  
 
1459
  tempdir_created = true;
 
1460
  if(mkdtemp(tempdir) == NULL){
 
1461
    tempdir_created = false;
 
1462
    perror("mkdtemp");
 
1463
    goto end;
 
1464
  }
 
1465
  
 
1466
  if(quit_now){
 
1467
    goto end;
 
1468
  }
 
1469
  
 
1470
  if(not init_gpgme(pubkey, seckey, tempdir)){
 
1471
    fprintf(stderr, "init_gpgme failed\n");
 
1472
    exitcode = EXIT_FAILURE;
 
1473
    goto end;
 
1474
  } else {
 
1475
    gpgme_initialized = true;
 
1476
  }
 
1477
  
 
1478
  if(quit_now){
 
1479
    goto end;
 
1480
  }
 
1481
  
 
1482
  if(connect_to != NULL){
 
1483
    /* Connect directly, do not use Zeroconf */
 
1484
    /* (Mainly meant for debugging) */
 
1485
    char *address = strrchr(connect_to, ':');
 
1486
    if(address == NULL){
 
1487
      fprintf(stderr, "No colon in address\n");
 
1488
      exitcode = EXIT_FAILURE;
 
1489
      goto end;
 
1490
    }
 
1491
    
 
1492
    if(quit_now){
 
1493
      goto end;
 
1494
    }
 
1495
    
 
1496
    uint16_t port;
 
1497
    errno = 0;
 
1498
    tmpmax = strtoimax(address+1, &tmp, 10);
 
1499
    if(errno != 0 or tmp == address+1 or *tmp != '\0'
 
1500
       or tmpmax != (uint16_t)tmpmax){
 
1501
      fprintf(stderr, "Bad port number\n");
 
1502
      exitcode = EXIT_FAILURE;
 
1503
      goto end;
 
1504
    }
 
1505
  
 
1506
    if(quit_now){
 
1507
      goto end;
 
1508
    }
 
1509
    
 
1510
    port = (uint16_t)tmpmax;
 
1511
    *address = '\0';
 
1512
    address = connect_to;
 
1513
    /* Colon in address indicates IPv6 */
 
1514
    int af;
 
1515
    if(strchr(address, ':') != NULL){
 
1516
      af = AF_INET6;
 
1517
    } else {
 
1518
      af = AF_INET;
 
1519
    }
 
1520
    
 
1521
    if(quit_now){
 
1522
      goto end;
 
1523
    }
 
1524
    
 
1525
    ret = start_mandos_communication(address, port, if_index, af);
 
1526
    if(ret < 0){
 
1527
      exitcode = EXIT_FAILURE;
 
1528
    } else {
 
1529
      exitcode = EXIT_SUCCESS;
 
1530
    }
 
1531
    goto end;
 
1532
  }
 
1533
  
 
1534
  if(quit_now){
 
1535
    goto end;
 
1536
  }
 
1537
  
 
1538
  {
507
1539
    AvahiServerConfig config;
508
 
    AvahiSServiceBrowser *sb = NULL;
509
 
    int error;
510
 
    int ret = 1;
511
 
 
512
 
    avahi_set_log_function(empty_log);
513
 
    
514
 
    /* Initialize the psuedo-RNG */
515
 
    srand(time(NULL));
516
 
 
517
 
    /* Allocate main loop object */
518
 
    if (!(simple_poll = avahi_simple_poll_new())) {
519
 
        fprintf(stderr, "Failed to create simple poll object.\n");
520
 
        goto fail;
521
 
    }
522
 
 
523
 
    /* Do not publish any local records */
 
1540
    /* Do not publish any local Zeroconf records */
524
1541
    avahi_server_config_init(&config);
525
1542
    config.publish_hinfo = 0;
526
1543
    config.publish_addresses = 0;
527
1544
    config.publish_workstation = 0;
528
1545
    config.publish_domain = 0;
529
 
 
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; */
534
1546
    
535
1547
    /* Allocate a new server */
536
 
    server = avahi_server_new(avahi_simple_poll_get(simple_poll), &config, NULL, NULL, &error);
537
 
 
538
 
    /* Free the configuration data */
 
1548
    mc.server = avahi_server_new(avahi_simple_poll_get
 
1549
                                 (mc.simple_poll), &config, NULL,
 
1550
                                 NULL, &error);
 
1551
    
 
1552
    /* Free the Avahi configuration data */
539
1553
    avahi_server_config_free(&config);
540
 
 
541
 
    /* Check wether creating the server object succeeded */
542
 
    if (!server) {
543
 
        fprintf(stderr, "Failed to create server: %s\n", avahi_strerror(error));
544
 
        goto fail;
545
 
    }
546
 
    
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)));
550
 
        goto fail;
551
 
    }
552
 
    
553
 
    /* Run the main loop */
554
 
    avahi_simple_poll_loop(simple_poll);
555
 
    
556
 
    ret = 0;
557
 
    
558
 
fail:
559
 
    
560
 
    /* Cleanup things */
561
 
    if (sb)
562
 
        avahi_s_service_browser_free(sb);
563
 
    
564
 
    if (server)
565
 
        avahi_server_free(server);
566
 
 
567
 
    if (simple_poll)
568
 
        avahi_simple_poll_free(simple_poll);
569
 
 
570
 
    return ret;
 
1554
  }
 
1555
  
 
1556
  /* Check if creating the Avahi server object succeeded */
 
1557
  if(mc.server == NULL){
 
1558
    fprintf(stderr, "Failed to create Avahi server: %s\n",
 
1559
            avahi_strerror(error));
 
1560
    exitcode = EXIT_FAILURE;
 
1561
    goto end;
 
1562
  }
 
1563
  
 
1564
  if(quit_now){
 
1565
    goto end;
 
1566
  }
 
1567
  
 
1568
  /* Create the Avahi service browser */
 
1569
  sb = avahi_s_service_browser_new(mc.server, if_index,
 
1570
                                   AVAHI_PROTO_UNSPEC, "_mandos._tcp",
 
1571
                                   NULL, 0, browse_callback, NULL);
 
1572
  if(sb == NULL){
 
1573
    fprintf(stderr, "Failed to create service browser: %s\n",
 
1574
            avahi_strerror(avahi_server_errno(mc.server)));
 
1575
    exitcode = EXIT_FAILURE;
 
1576
    goto end;
 
1577
  }
 
1578
  
 
1579
  if(quit_now){
 
1580
    goto end;
 
1581
  }
 
1582
  
 
1583
  /* Run the main loop */
 
1584
  
 
1585
  if(debug){
 
1586
    fprintf(stderr, "Starting Avahi loop search\n");
 
1587
  }
 
1588
  
 
1589
  avahi_simple_poll_loop(mc.simple_poll);
 
1590
  
 
1591
 end:
 
1592
  
 
1593
  if(debug){
 
1594
    fprintf(stderr, "%s exiting\n", argv[0]);
 
1595
  }
 
1596
  
 
1597
  /* Cleanup things */
 
1598
  if(sb != NULL)
 
1599
    avahi_s_service_browser_free(sb);
 
1600
  
 
1601
  if(mc.server != NULL)
 
1602
    avahi_server_free(mc.server);
 
1603
  
 
1604
  if(mc.simple_poll != NULL)
 
1605
    avahi_simple_poll_free(mc.simple_poll);
 
1606
  
 
1607
  if(gnutls_initialized){
 
1608
    gnutls_certificate_free_credentials(mc.cred);
 
1609
    gnutls_global_deinit();
 
1610
    gnutls_dh_params_deinit(mc.dh_params);
 
1611
  }
 
1612
  
 
1613
  if(gpgme_initialized){
 
1614
    gpgme_release(mc.ctx);
 
1615
  }
 
1616
  
 
1617
  /* Take down the network interface */
 
1618
  if(take_down_interface){
 
1619
    /* Re-raise priviliges */
 
1620
    errno = 0;
 
1621
    ret = seteuid(0);
 
1622
    if(ret == -1){
 
1623
      perror("seteuid");
 
1624
    }
 
1625
    if(geteuid() == 0){
 
1626
      ret = ioctl(sd, SIOCGIFFLAGS, &network);
 
1627
      if(ret == -1){
 
1628
        perror("ioctl SIOCGIFFLAGS");
 
1629
      } else if(network.ifr_flags & IFF_UP) {
 
1630
        network.ifr_flags &= ~IFF_UP; /* clear flag */
 
1631
        ret = ioctl(sd, SIOCSIFFLAGS, &network);
 
1632
        if(ret == -1){
 
1633
          perror("ioctl SIOCSIFFLAGS");
 
1634
        }
 
1635
      }
 
1636
      ret = (int)TEMP_FAILURE_RETRY(close(sd));
 
1637
      if(ret == -1){
 
1638
        perror("close");
 
1639
      }
 
1640
      /* Lower privileges permanently */
 
1641
      errno = 0;
 
1642
      ret = setuid(uid);
 
1643
      if(ret == -1){
 
1644
        perror("setuid");
 
1645
      }
 
1646
    }
 
1647
  }
 
1648
  
 
1649
  /* Removes the temp directory used by GPGME */
 
1650
  if(tempdir_created){
 
1651
    DIR *d;
 
1652
    struct dirent *direntry;
 
1653
    d = opendir(tempdir);
 
1654
    if(d == NULL){
 
1655
      if(errno != ENOENT){
 
1656
        perror("opendir");
 
1657
      }
 
1658
    } else {
 
1659
      while(true){
 
1660
        direntry = readdir(d);
 
1661
        if(direntry == NULL){
 
1662
          break;
 
1663
        }
 
1664
        /* Skip "." and ".." */
 
1665
        if(direntry->d_name[0] == '.'
 
1666
           and (direntry->d_name[1] == '\0'
 
1667
                or (direntry->d_name[1] == '.'
 
1668
                    and direntry->d_name[2] == '\0'))){
 
1669
          continue;
 
1670
        }
 
1671
        char *fullname = NULL;
 
1672
        ret = asprintf(&fullname, "%s/%s", tempdir,
 
1673
                       direntry->d_name);
 
1674
        if(ret < 0){
 
1675
          perror("asprintf");
 
1676
          continue;
 
1677
        }
 
1678
        ret = remove(fullname);
 
1679
        if(ret == -1){
 
1680
          fprintf(stderr, "remove(\"%s\"): %s\n", fullname,
 
1681
                  strerror(errno));
 
1682
        }
 
1683
        free(fullname);
 
1684
      }
 
1685
      closedir(d);
 
1686
    }
 
1687
    ret = rmdir(tempdir);
 
1688
    if(ret == -1 and errno != ENOENT){
 
1689
      perror("rmdir");
 
1690
    }
 
1691
  }
 
1692
  
 
1693
  if(quit_now){
 
1694
    sigemptyset(&old_sigterm_action.sa_mask);
 
1695
    old_sigterm_action.sa_handler = SIG_DFL;
 
1696
    ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received,
 
1697
                                            &old_sigterm_action,
 
1698
                                            NULL));
 
1699
    if(ret == -1){
 
1700
      perror("sigaction");
 
1701
    }
 
1702
    do {
 
1703
      ret = raise(signal_received);
 
1704
    } while(ret != 0 and errno == EINTR);
 
1705
    if(ret != 0){
 
1706
      perror("raise");
 
1707
      abort();
 
1708
    }
 
1709
    TEMP_FAILURE_RETRY(pause());
 
1710
  }
 
1711
  
 
1712
  return exitcode;
571
1713
}