/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

* plugins.d/mandos-client.c (run_network_hooks): New.
  (main): Run run_network_hooks() with "stop" and "start" at
          appropriate places.  Don't lower privileges permanently
          prematurely.

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