/mandos/trunk

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

« back to all changes in this revision

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

  • Committer: teddy at bsnet
  • Date: 2011-03-21 19:22:27 UTC
  • Revision ID: teddy@fukt.bsnet.se-20110321192227-y74ngvvcy9eq6shi
* plugins.d/mandos-client.c (good_interface): Check if the interface
                                              name starts with "."
                                              before anything else.
                                              Bug fix: Fix memory
                                              leak.

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