/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-10 06:28:14 UTC
  • Revision ID: teddy@fukt.bsnet.se-20090910062814-wkkngiwm0xhxwlm2
* init.d-mandos: Bug fix: Correct the LSB header.

* mandos.xml (CHECKING): Add text about client disabling requiring
                         manual intervention to reverse.

* plugin-runner.c: Cast return value of TEMP_FAILURE_RETRY to "int"
                   where necessary.

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