/mandos/trunk

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

« back to all changes in this revision

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

  • Committer: Teddy Hogeborn
  • Date: 2009-09-22 12:48:07 UTC
  • Revision ID: teddy@fukt.bsnet.se-20090922124807-i2cr0zv4vxjv38g4
* mandos-ctl: Made work again after D-Bus API changes.
  (datetime_to_milliseconds): Renamed to "timedelta_to_milliseconds".
                              All callers changed.
  (milliseconds_to_string): Use clearer mapping string format.
  (string_to_delta): Add some comments.

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