/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

merge

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