/mandos/release

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

« back to all changes in this revision

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

  • Committer: Björn Påhlsson
  • Date: 2011-06-18 22:51:12 UTC
  • mto: (237.7.33 trunk)
  • mto: This revision was merged to the branch mainline in revision 284.
  • Revision ID: belorn@fukt.bsnet.se-20110618225112-rnt2m4ek32758f80
using scandir instead of readdir

Show diffs side-by-side

added added

removed removed

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