/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 Hogeborn
  • Date: 2011-09-19 09:42:55 UTC
  • Revision ID: teddy@fukt.bsnet.se-20110919094255-3lji3igz75uog8lh
* mandos-clients.conf.xml (OPTIONS): Moved up "extended_timeout" to
                                     order options alphabetically.
* clients.conf: Language change in comment.

Show diffs side-by-side

added added

removed removed

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