/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-08-30 03:10:29 UTC
  • Revision ID: teddy@fukt.bsnet.se-20090830031029-cj8po3rc4g1cux1d
* mandos: White-space fixes only.
* mandos-ctl: - '' -
* plugin-runner.c: - '' -
* plugins.d/askpass-fifo.c: - '' -
* plugins.d/mandos-client.c: - '' -
* plugins.d/password-prompt.c: - '' -

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(), setuid(),
 
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){
 
167
  int ret;
74
168
  gpgme_error_t rc;
75
 
  ssize_t ret;
76
 
  size_t new_packet_capacity = 0;
77
 
  size_t new_packet_length = 0;
78
169
  gpgme_engine_info_t engine_info;
79
 
 
80
 
  if (debug){
81
 
    fprintf(stderr, "Attempting to decrypt password from gpg packet\n");
 
170
  
 
171
  
 
172
  /*
 
173
   * Helper function to insert pub and seckey to the engine keyring.
 
174
   */
 
175
  bool import_key(const char *filename){
 
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){
 
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){
315
524
  int ret, tcp_sd;
316
 
  struct sockaddr_in6 to;
317
 
  struct in6_addr ip_addr;
318
 
  encrypted_session es;
 
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
534
  ssize_t decrypted_buffer_size;
 
535
  size_t written;
324
536
  int retval = 0;
325
 
  const char interface[] = "eth0";
326
 
 
 
537
  gnutls_session_t session;
 
538
  int pf;                       /* Protocol family */
 
539
  
 
540
  switch(af){
 
541
  case AF_INET6:
 
542
    pf = PF_INET6;
 
543
    break;
 
544
  case AF_INET:
 
545
    pf = PF_INET;
 
546
    break;
 
547
  default:
 
548
    fprintf(stderr, "Bad address family: %d\n", af);
 
549
    return -1;
 
550
  }
 
551
  
 
552
  ret = init_gnutls_session(&session);
 
553
  if(ret != 0){
 
554
    return -1;
 
555
  }
 
556
  
327
557
  if(debug){
328
 
    fprintf(stderr, "Setting up a tcp connection to %s\n", ip);
 
558
    fprintf(stderr, "Setting up a TCP connection to %s, port %" PRIu16
 
559
            "\n", ip, port);
329
560
  }
330
561
  
331
 
  tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
332
 
  if(tcp_sd < 0) {
 
562
  tcp_sd = socket(pf, SOCK_STREAM, 0);
 
563
  if(tcp_sd < 0){
333
564
    perror("socket");
334
565
    return -1;
335
566
  }
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
567
  
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
  memset(&to, 0, sizeof(to));
 
569
  if(af == AF_INET6){
 
570
    to.in6.sin6_family = (sa_family_t)af;
 
571
    ret = inet_pton(af, ip, &to.in6.sin6_addr);
 
572
  } else {                      /* IPv4 */
 
573
    to.in.sin_family = (sa_family_t)af;
 
574
    ret = inet_pton(af, ip, &to.in.sin_addr);
 
575
  }
 
576
  if(ret < 0 ){
351
577
    perror("inet_pton");
352
578
    return -1;
353
 
  }  
 
579
  }
354
580
  if(ret == 0){
355
581
    fprintf(stderr, "Bad address: %s\n", ip);
356
582
    return -1;
357
583
  }
358
 
  to.sin6_port = htons(port);
359
 
  to.sin6_scope_id = if_nametoindex(interface);
360
 
 
 
584
  if(af == AF_INET6){
 
585
    to.in6.sin6_port = htons(port); /* Spurious warnings from
 
586
                                       -Wconversion and
 
587
                                       -Wunreachable-code */
 
588
    
 
589
    if(IN6_IS_ADDR_LINKLOCAL /* Spurious warnings from */
 
590
       (&to.in6.sin6_addr)){ /* -Wstrict-aliasing=2 or lower and
 
591
                              -Wunreachable-code*/
 
592
      if(if_index == AVAHI_IF_UNSPEC){
 
593
        fprintf(stderr, "An IPv6 link-local address is incomplete"
 
594
                " without a network interface\n");
 
595
        return -1;
 
596
      }
 
597
      /* Set the network interface number as scope */
 
598
      to.in6.sin6_scope_id = (uint32_t)if_index;
 
599
    }
 
600
  } else {
 
601
    to.in.sin_port = htons(port); /* Spurious warnings from
 
602
                                     -Wconversion and
 
603
                                     -Wunreachable-code */
 
604
  }
 
605
  
361
606
  if(debug){
362
 
    fprintf(stderr, "Connection to: %s\n", ip);
 
607
    if(af == AF_INET6 and if_index != AVAHI_IF_UNSPEC){
 
608
      char interface[IF_NAMESIZE];
 
609
      if(if_indextoname((unsigned int)if_index, interface) == NULL){
 
610
        perror("if_indextoname");
 
611
      } else {
 
612
        fprintf(stderr, "Connection to: %s%%%s, port %" PRIu16 "\n",
 
613
                ip, interface, port);
 
614
      }
 
615
    } else {
 
616
      fprintf(stderr, "Connection to: %s, port %" PRIu16 "\n", ip,
 
617
              port);
 
618
    }
 
619
    char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ?
 
620
                 INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = "";
 
621
    const char *pcret;
 
622
    if(af == AF_INET6){
 
623
      pcret = inet_ntop(af, &(to.in6.sin6_addr), addrstr,
 
624
                        sizeof(addrstr));
 
625
    } else {
 
626
      pcret = inet_ntop(af, &(to.in.sin_addr), addrstr,
 
627
                        sizeof(addrstr));
 
628
    }
 
629
    if(pcret == NULL){
 
630
      perror("inet_ntop");
 
631
    } else {
 
632
      if(strcmp(addrstr, ip) != 0){
 
633
        fprintf(stderr, "Canonical address form: %s\n", addrstr);
 
634
      }
 
635
    }
363
636
  }
364
637
  
365
 
  ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
366
 
  if (ret < 0){
 
638
  if(af == AF_INET6){
 
639
    ret = connect(tcp_sd, &to.in6, sizeof(to));
 
640
  } else {
 
641
    ret = connect(tcp_sd, &to.in, sizeof(to)); /* IPv4 */
 
642
  }
 
643
  if(ret < 0){
367
644
    perror("connect");
368
645
    return -1;
369
646
  }
370
647
  
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
 
 
 
648
  const char *out = mandos_protocol_version;
 
649
  written = 0;
400
650
  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;
 
651
    size_t out_size = strlen(out);
 
652
    ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
 
653
                                   out_size - written));
 
654
    if(ret == -1){
 
655
      perror("write");
 
656
      retval = -1;
 
657
      goto mandos_end;
 
658
    }
 
659
    written += (size_t)ret;
 
660
    if(written < out_size){
 
661
      continue;
 
662
    } else {
 
663
      if(out == mandos_protocol_version){
 
664
        written = 0;
 
665
        out = "\r\n";
 
666
      } else {
 
667
        break;
406
668
      }
407
 
      buffer_capacity += BUFFER_SIZE;
 
669
    }
 
670
  }
 
671
  
 
672
  if(debug){
 
673
    fprintf(stderr, "Establishing TLS session with %s\n", ip);
 
674
  }
 
675
  
 
676
  gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t) tcp_sd);
 
677
  
 
678
  do{
 
679
    ret = gnutls_handshake(session);
 
680
  } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
 
681
  
 
682
  if(ret != GNUTLS_E_SUCCESS){
 
683
    if(debug){
 
684
      fprintf(stderr, "*** GnuTLS Handshake failed ***\n");
 
685
      gnutls_perror(ret);
 
686
    }
 
687
    retval = -1;
 
688
    goto mandos_end;
 
689
  }
 
690
  
 
691
  /* Read OpenPGP packet that contains the wanted password */
 
692
  
 
693
  if(debug){
 
694
    fprintf(stderr, "Retrieving OpenPGP encrypted password from %s\n",
 
695
            ip);
 
696
  }
 
697
  
 
698
  while(true){
 
699
    buffer_capacity = incbuffer(&buffer, buffer_length,
 
700
                                   buffer_capacity);
 
701
    if(buffer_capacity == 0){
 
702
      perror("incbuffer");
 
703
      retval = -1;
 
704
      goto mandos_end;
408
705
    }
409
706
    
410
 
    ret = gnutls_record_recv
411
 
      (es.session, buffer+buffer_length, BUFFER_SIZE);
412
 
    if (ret == 0){
 
707
    sret = gnutls_record_recv(session, buffer+buffer_length,
 
708
                              BUFFER_SIZE);
 
709
    if(sret == 0){
413
710
      break;
414
711
    }
415
 
    if (ret < 0){
416
 
      switch(ret){
 
712
    if(sret < 0){
 
713
      switch(sret){
417
714
      case GNUTLS_E_INTERRUPTED:
418
715
      case GNUTLS_E_AGAIN:
419
716
        break;
420
717
      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);
 
718
        do{
 
719
          ret = gnutls_handshake(session);
 
720
        } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
 
721
        if(ret < 0){
 
722
          fprintf(stderr, "*** GnuTLS Re-handshake failed ***\n");
 
723
          gnutls_perror(ret);
425
724
          retval = -1;
426
 
          goto exit;
 
725
          goto mandos_end;
427
726
        }
428
727
        break;
429
728
      default:
430
 
        fprintf(stderr, "Unknown error while reading data from encrypted session with mandos server\n");
 
729
        fprintf(stderr, "Unknown error while reading data from"
 
730
                " encrypted session with Mandos server\n");
431
731
        retval = -1;
432
 
        gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
433
 
        goto exit;
 
732
        gnutls_bye(session, GNUTLS_SHUT_RDWR);
 
733
        goto mandos_end;
434
734
      }
435
735
    } else {
436
 
      buffer_length += ret;
 
736
      buffer_length += (size_t) sret;
437
737
    }
438
738
  }
439
739
  
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);
 
740
  if(debug){
 
741
    fprintf(stderr, "Closing TLS session\n");
 
742
  }
 
743
  
 
744
  gnutls_bye(session, GNUTLS_SHUT_RDWR);
 
745
  
 
746
  if(buffer_length > 0){
 
747
    decrypted_buffer_size = pgp_packet_decrypt(buffer,
 
748
                                               buffer_length,
 
749
                                               &decrypted_buffer);
 
750
    if(decrypted_buffer_size >= 0){
 
751
      written = 0;
 
752
      while(written < (size_t) decrypted_buffer_size){
 
753
        ret = (int)fwrite(decrypted_buffer + written, 1,
 
754
                          (size_t)decrypted_buffer_size - written,
 
755
                          stdout);
 
756
        if(ret == 0 and ferror(stdout)){
 
757
          if(debug){
 
758
            fprintf(stderr, "Error writing encrypted data: %s\n",
 
759
                    strerror(errno));
 
760
          }
 
761
          retval = -1;
 
762
          break;
 
763
        }
 
764
        written += (size_t)ret;
 
765
      }
443
766
      free(decrypted_buffer);
444
767
    } else {
445
768
      retval = -1;
446
769
    }
447
 
  }
448
 
 
449
 
  //shutdown procedure
450
 
 
451
 
  if(debug){
452
 
    fprintf(stderr, "Closing tls session\n");
453
 
  }
454
 
 
 
770
  } else {
 
771
    retval = -1;
 
772
  }
 
773
  
 
774
  /* Shutdown procedure */
 
775
  
 
776
 mandos_end:
455
777
  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 ();
 
778
  ret = (int)TEMP_FAILURE_RETRY(close(tcp_sd));
 
779
  if(ret == -1){
 
780
    perror("close");
 
781
  }
 
782
  gnutls_deinit(session);
462
783
  return retval;
463
784
}
464
785
 
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[]) {
 
786
static void resolve_callback(AvahiSServiceResolver *r,
 
787
                             AvahiIfIndex interface,
 
788
                             AvahiProtocol proto,
 
789
                             AvahiResolverEvent event,
 
790
                             const char *name,
 
791
                             const char *type,
 
792
                             const char *domain,
 
793
                             const char *host_name,
 
794
                             const AvahiAddress *address,
 
795
                             uint16_t port,
 
796
                             AVAHI_GCC_UNUSED AvahiStringList *txt,
 
797
                             AVAHI_GCC_UNUSED AvahiLookupResultFlags
 
798
                             flags,
 
799
                             AVAHI_GCC_UNUSED void* userdata){
 
800
  assert(r);
 
801
  
 
802
  /* Called whenever a service has been resolved successfully or
 
803
     timed out */
 
804
  
 
805
  if(quit_now){
 
806
    return;
 
807
  }
 
808
  
 
809
  switch(event){
 
810
  default:
 
811
  case AVAHI_RESOLVER_FAILURE:
 
812
    fprintf(stderr, "(Avahi Resolver) Failed to resolve service '%s'"
 
813
            " of type '%s' in domain '%s': %s\n", name, type, domain,
 
814
            avahi_strerror(avahi_server_errno(mc.server)));
 
815
    break;
 
816
    
 
817
  case AVAHI_RESOLVER_FOUND:
 
818
    {
 
819
      char ip[AVAHI_ADDRESS_STR_MAX];
 
820
      avahi_address_snprint(ip, sizeof(ip), address);
 
821
      if(debug){
 
822
        fprintf(stderr, "Mandos server \"%s\" found on %s (%s, %"
 
823
                PRIdMAX ") on port %" PRIu16 "\n", name, host_name,
 
824
                ip, (intmax_t)interface, port);
 
825
      }
 
826
      int ret = start_mandos_communication(ip, port, interface,
 
827
                                           avahi_proto_to_af(proto));
 
828
      if(ret == 0){
 
829
        avahi_simple_poll_quit(mc.simple_poll);
 
830
      }
 
831
    }
 
832
  }
 
833
  avahi_s_service_resolver_free(r);
 
834
}
 
835
 
 
836
static void browse_callback(AvahiSServiceBrowser *b,
 
837
                            AvahiIfIndex interface,
 
838
                            AvahiProtocol protocol,
 
839
                            AvahiBrowserEvent event,
 
840
                            const char *name,
 
841
                            const char *type,
 
842
                            const char *domain,
 
843
                            AVAHI_GCC_UNUSED AvahiLookupResultFlags
 
844
                            flags,
 
845
                            AVAHI_GCC_UNUSED void* userdata){
 
846
  assert(b);
 
847
  
 
848
  /* Called whenever a new services becomes available on the LAN or
 
849
     is removed from the LAN */
 
850
  
 
851
  switch(event){
 
852
  default:
 
853
  case AVAHI_BROWSER_FAILURE:
 
854
    
 
855
    fprintf(stderr, "(Avahi browser) %s\n",
 
856
            avahi_strerror(avahi_server_errno(mc.server)));
 
857
    avahi_simple_poll_quit(mc.simple_poll);
 
858
    return;
 
859
    
 
860
  case AVAHI_BROWSER_NEW:
 
861
    /* We ignore the returned Avahi resolver object. In the callback
 
862
       function we free it. If the Avahi server is terminated before
 
863
       the callback function is called the Avahi server will free the
 
864
       resolver for us. */
 
865
    
 
866
    if(avahi_s_service_resolver_new(mc.server, interface, protocol,
 
867
                                    name, type, domain, protocol, 0,
 
868
                                    resolve_callback, NULL) == NULL)
 
869
      fprintf(stderr, "Avahi: Failed to resolve service '%s': %s\n",
 
870
              name, avahi_strerror(avahi_server_errno(mc.server)));
 
871
    break;
 
872
    
 
873
  case AVAHI_BROWSER_REMOVE:
 
874
    break;
 
875
    
 
876
  case AVAHI_BROWSER_ALL_FOR_NOW:
 
877
  case AVAHI_BROWSER_CACHE_EXHAUSTED:
 
878
    if(debug){
 
879
      fprintf(stderr, "No Mandos server found, still searching...\n");
 
880
    }
 
881
    break;
 
882
  }
 
883
}
 
884
 
 
885
/* stop main loop after sigterm has been called */
 
886
static void handle_sigterm(int sig){
 
887
  if(quit_now){
 
888
    return;
 
889
  }
 
890
  quit_now = 1;
 
891
  signal_received = sig;
 
892
  int old_errno = errno;
 
893
  if(mc.simple_poll != NULL){
 
894
    avahi_simple_poll_quit(mc.simple_poll);
 
895
  }
 
896
  errno = old_errno;
 
897
}
 
898
 
 
899
int main(int argc, char *argv[]){
 
900
  AvahiSServiceBrowser *sb = NULL;
 
901
  int error;
 
902
  int ret;
 
903
  intmax_t tmpmax;
 
904
  char *tmp;
 
905
  int exitcode = EXIT_SUCCESS;
 
906
  const char *interface = "eth0";
 
907
  struct ifreq network;
 
908
  int sd = -1;
 
909
  bool take_down_interface = false;
 
910
  uid_t uid;
 
911
  gid_t gid;
 
912
  char *connect_to = NULL;
 
913
  char tempdir[] = "/tmp/mandosXXXXXX";
 
914
  bool tempdir_created = false;
 
915
  AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
 
916
  const char *seckey = PATHDIR "/" SECKEY;
 
917
  const char *pubkey = PATHDIR "/" PUBKEY;
 
918
  
 
919
  bool gnutls_initialized = false;
 
920
  bool gpgme_initialized = false;
 
921
  float delay = 2.5f;
 
922
  
 
923
  struct sigaction old_sigterm_action;
 
924
  struct sigaction sigterm_action = { .sa_handler = handle_sigterm };
 
925
  
 
926
  {
 
927
    struct argp_option options[] = {
 
928
      { .name = "debug", .key = 128,
 
929
        .doc = "Debug mode", .group = 3 },
 
930
      { .name = "connect", .key = 'c',
 
931
        .arg = "ADDRESS:PORT",
 
932
        .doc = "Connect directly to a specific Mandos server",
 
933
        .group = 1 },
 
934
      { .name = "interface", .key = 'i',
 
935
        .arg = "NAME",
 
936
        .doc = "Network interface that will be used to search for"
 
937
        " Mandos servers",
 
938
        .group = 1 },
 
939
      { .name = "seckey", .key = 's',
 
940
        .arg = "FILE",
 
941
        .doc = "OpenPGP secret key file base name",
 
942
        .group = 1 },
 
943
      { .name = "pubkey", .key = 'p',
 
944
        .arg = "FILE",
 
945
        .doc = "OpenPGP public key file base name",
 
946
        .group = 2 },
 
947
      { .name = "dh-bits", .key = 129,
 
948
        .arg = "BITS",
 
949
        .doc = "Bit length of the prime number used in the"
 
950
        " Diffie-Hellman key exchange",
 
951
        .group = 2 },
 
952
      { .name = "priority", .key = 130,
 
953
        .arg = "STRING",
 
954
        .doc = "GnuTLS priority string for the TLS handshake",
 
955
        .group = 1 },
 
956
      { .name = "delay", .key = 131,
 
957
        .arg = "SECONDS",
 
958
        .doc = "Maximum delay to wait for interface startup",
 
959
        .group = 2 },
 
960
      { .name = NULL }
 
961
    };
 
962
    
 
963
    error_t parse_opt(int key, char *arg,
 
964
                      struct argp_state *state){
 
965
      switch(key){
 
966
      case 128:                 /* --debug */
 
967
        debug = true;
 
968
        break;
 
969
      case 'c':                 /* --connect */
 
970
        connect_to = arg;
 
971
        break;
 
972
      case 'i':                 /* --interface */
 
973
        interface = arg;
 
974
        break;
 
975
      case 's':                 /* --seckey */
 
976
        seckey = arg;
 
977
        break;
 
978
      case 'p':                 /* --pubkey */
 
979
        pubkey = arg;
 
980
        break;
 
981
      case 129:                 /* --dh-bits */
 
982
        errno = 0;
 
983
        tmpmax = strtoimax(arg, &tmp, 10);
 
984
        if(errno != 0 or tmp == arg or *tmp != '\0'
 
985
           or tmpmax != (typeof(mc.dh_bits))tmpmax){
 
986
          fprintf(stderr, "Bad number of DH bits\n");
 
987
          exit(EXIT_FAILURE);
 
988
        }
 
989
        mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
 
990
        break;
 
991
      case 130:                 /* --priority */
 
992
        mc.priority = arg;
 
993
        break;
 
994
      case 131:                 /* --delay */
 
995
        errno = 0;
 
996
        delay = strtof(arg, &tmp);
 
997
        if(errno != 0 or tmp == arg or *tmp != '\0'){
 
998
          fprintf(stderr, "Bad delay\n");
 
999
          exit(EXIT_FAILURE);
 
1000
        }
 
1001
        break;
 
1002
      case ARGP_KEY_ARG:
 
1003
        argp_usage(state);
 
1004
      case ARGP_KEY_END:
 
1005
        break;
 
1006
      default:
 
1007
        return ARGP_ERR_UNKNOWN;
 
1008
      }
 
1009
      return 0;
 
1010
    }
 
1011
    
 
1012
    struct argp argp = { .options = options, .parser = parse_opt,
 
1013
                         .args_doc = "",
 
1014
                         .doc = "Mandos client -- Get and decrypt"
 
1015
                         " passwords from a Mandos server" };
 
1016
    ret = argp_parse(&argp, argc, argv, 0, 0, NULL);
 
1017
    if(ret == ARGP_ERR_UNKNOWN){
 
1018
      fprintf(stderr, "Unknown error while parsing arguments\n");
 
1019
      exitcode = EXIT_FAILURE;
 
1020
      goto end;
 
1021
    }
 
1022
  }
 
1023
  
 
1024
  if(not debug){
 
1025
    avahi_set_log_function(empty_log);
 
1026
  }
 
1027
  
 
1028
  /* Initialize Avahi early so avahi_simple_poll_quit() can be called
 
1029
     from the signal handler */
 
1030
  /* Initialize the pseudo-RNG for Avahi */
 
1031
  srand((unsigned int) time(NULL));
 
1032
  mc.simple_poll = avahi_simple_poll_new();
 
1033
  if(mc.simple_poll == NULL){
 
1034
    fprintf(stderr, "Avahi: Failed to create simple poll object.\n");
 
1035
    exitcode = EXIT_FAILURE;
 
1036
    goto end;
 
1037
  }
 
1038
  
 
1039
  sigemptyset(&sigterm_action.sa_mask);
 
1040
  ret = sigaddset(&sigterm_action.sa_mask, SIGINT);
 
1041
  if(ret == -1){
 
1042
    perror("sigaddset");
 
1043
    exitcode = EXIT_FAILURE;
 
1044
    goto end;
 
1045
  }
 
1046
  ret = sigaddset(&sigterm_action.sa_mask, SIGHUP);
 
1047
  if(ret == -1){
 
1048
    perror("sigaddset");
 
1049
    exitcode = EXIT_FAILURE;
 
1050
    goto end;
 
1051
  }
 
1052
  ret = sigaddset(&sigterm_action.sa_mask, SIGTERM);
 
1053
  if(ret == -1){
 
1054
    perror("sigaddset");
 
1055
    exitcode = EXIT_FAILURE;
 
1056
    goto end;
 
1057
  }
 
1058
  ret = sigaction(SIGINT, &sigterm_action, &old_sigterm_action);
 
1059
  if(ret == -1){
 
1060
    perror("sigaction");
 
1061
    exitcode = EXIT_FAILURE;
 
1062
    goto end;
 
1063
  }
 
1064
  ret = sigaction(SIGHUP, &sigterm_action, NULL);
 
1065
  if(ret == -1){
 
1066
    perror("sigaction");
 
1067
    exitcode = EXIT_FAILURE;
 
1068
    goto end;
 
1069
  }
 
1070
  ret = sigaction(SIGTERM, &sigterm_action, NULL);
 
1071
  if(ret == -1){
 
1072
    perror("sigaction");
 
1073
    exitcode = EXIT_FAILURE;
 
1074
    goto end;
 
1075
  }
 
1076
  
 
1077
  /* If the interface is down, bring it up */
 
1078
  if(interface[0] != '\0'){
 
1079
    if_index = (AvahiIfIndex) if_nametoindex(interface);
 
1080
    if(if_index == 0){
 
1081
      fprintf(stderr, "No such interface: \"%s\"\n", interface);
 
1082
      exitcode = EXIT_FAILURE;
 
1083
      goto end;
 
1084
    }
 
1085
    
 
1086
    if(quit_now){
 
1087
      goto end;
 
1088
    }
 
1089
    
 
1090
#ifdef __linux__
 
1091
    /* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
 
1092
       messages to mess up the prompt */
 
1093
    ret = klogctl(8, NULL, 5);
 
1094
    bool restore_loglevel = true;
 
1095
    if(ret == -1){
 
1096
      restore_loglevel = false;
 
1097
      perror("klogctl");
 
1098
    }
 
1099
#endif  /* __linux__ */
 
1100
    
 
1101
    sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
 
1102
    if(sd < 0){
 
1103
      perror("socket");
 
1104
      exitcode = EXIT_FAILURE;
 
1105
#ifdef __linux__
 
1106
      if(restore_loglevel){
 
1107
        ret = klogctl(7, NULL, 0);
 
1108
        if(ret == -1){
 
1109
          perror("klogctl");
 
1110
        }
 
1111
      }
 
1112
#endif  /* __linux__ */
 
1113
      goto end;
 
1114
    }
 
1115
    strcpy(network.ifr_name, interface);
 
1116
    ret = ioctl(sd, SIOCGIFFLAGS, &network);
 
1117
    if(ret == -1){
 
1118
      perror("ioctl SIOCGIFFLAGS");
 
1119
#ifdef __linux__
 
1120
      if(restore_loglevel){
 
1121
        ret = klogctl(7, NULL, 0);
 
1122
        if(ret == -1){
 
1123
          perror("klogctl");
 
1124
        }
 
1125
      }
 
1126
#endif  /* __linux__ */
 
1127
      exitcode = EXIT_FAILURE;
 
1128
      goto end;
 
1129
    }
 
1130
    if((network.ifr_flags & IFF_UP) == 0){
 
1131
      network.ifr_flags |= IFF_UP;
 
1132
      take_down_interface = true;
 
1133
      ret = ioctl(sd, SIOCSIFFLAGS, &network);
 
1134
      if(ret == -1){
 
1135
        take_down_interface = false;
 
1136
        perror("ioctl SIOCSIFFLAGS");
 
1137
        exitcode = EXIT_FAILURE;
 
1138
#ifdef __linux__
 
1139
        if(restore_loglevel){
 
1140
          ret = klogctl(7, NULL, 0);
 
1141
          if(ret == -1){
 
1142
            perror("klogctl");
 
1143
          }
 
1144
        }
 
1145
#endif  /* __linux__ */
 
1146
        goto end;
 
1147
      }
 
1148
    }
 
1149
    /* sleep checking until interface is running */
 
1150
    for(int i=0; i < delay * 4; i++){
 
1151
      ret = ioctl(sd, SIOCGIFFLAGS, &network);
 
1152
      if(ret == -1){
 
1153
        perror("ioctl SIOCGIFFLAGS");
 
1154
      } else if(network.ifr_flags & IFF_RUNNING){
 
1155
        break;
 
1156
      }
 
1157
      struct timespec sleeptime = { .tv_nsec = 250000000 };
 
1158
      ret = nanosleep(&sleeptime, NULL);
 
1159
      if(ret == -1 and errno != EINTR){
 
1160
        perror("nanosleep");
 
1161
      }
 
1162
    }
 
1163
    if(not take_down_interface){
 
1164
      /* We won't need the socket anymore */
 
1165
      ret = (int)TEMP_FAILURE_RETRY(close(sd));
 
1166
      if(ret == -1){
 
1167
        perror("close");
 
1168
      }
 
1169
    }
 
1170
#ifdef __linux__
 
1171
    if(restore_loglevel){
 
1172
      /* Restores kernel loglevel to default */
 
1173
      ret = klogctl(7, NULL, 0);
 
1174
      if(ret == -1){
 
1175
        perror("klogctl");
 
1176
      }
 
1177
    }
 
1178
#endif  /* __linux__ */
 
1179
  }
 
1180
  
 
1181
  if(quit_now){
 
1182
    goto end;
 
1183
  }
 
1184
  
 
1185
  uid = getuid();
 
1186
  gid = getgid();
 
1187
  
 
1188
  errno = 0;
 
1189
  setgid(gid);
 
1190
  if(ret == -1){
 
1191
    perror("setgid");
 
1192
  }
 
1193
  
 
1194
  ret = setuid(uid);
 
1195
  if(ret == -1){
 
1196
    perror("setuid");
 
1197
  }
 
1198
  
 
1199
  if(quit_now){
 
1200
    goto end;
 
1201
  }
 
1202
  
 
1203
  ret = init_gnutls_global(pubkey, seckey);
 
1204
  if(ret == -1){
 
1205
    fprintf(stderr, "init_gnutls_global failed\n");
 
1206
    exitcode = EXIT_FAILURE;
 
1207
    goto end;
 
1208
  } else {
 
1209
    gnutls_initialized = true;
 
1210
  }
 
1211
  
 
1212
  if(quit_now){
 
1213
    goto end;
 
1214
  }
 
1215
  
 
1216
  tempdir_created = true;
 
1217
  if(mkdtemp(tempdir) == NULL){
 
1218
    tempdir_created = false;
 
1219
    perror("mkdtemp");
 
1220
    goto end;
 
1221
  }
 
1222
  
 
1223
  if(quit_now){
 
1224
    goto end;
 
1225
  }
 
1226
  
 
1227
  if(not init_gpgme(pubkey, seckey, tempdir)){
 
1228
    fprintf(stderr, "init_gpgme failed\n");
 
1229
    exitcode = EXIT_FAILURE;
 
1230
    goto end;
 
1231
  } else {
 
1232
    gpgme_initialized = true;
 
1233
  }
 
1234
  
 
1235
  if(quit_now){
 
1236
    goto end;
 
1237
  }
 
1238
  
 
1239
  if(connect_to != NULL){
 
1240
    /* Connect directly, do not use Zeroconf */
 
1241
    /* (Mainly meant for debugging) */
 
1242
    char *address = strrchr(connect_to, ':');
 
1243
    if(address == NULL){
 
1244
      fprintf(stderr, "No colon in address\n");
 
1245
      exitcode = EXIT_FAILURE;
 
1246
      goto end;
 
1247
    }
 
1248
    
 
1249
    if(quit_now){
 
1250
      goto end;
 
1251
    }
 
1252
    
 
1253
    uint16_t port;
 
1254
    errno = 0;
 
1255
    tmpmax = strtoimax(address+1, &tmp, 10);
 
1256
    if(errno != 0 or tmp == address+1 or *tmp != '\0'
 
1257
       or tmpmax != (uint16_t)tmpmax){
 
1258
      fprintf(stderr, "Bad port number\n");
 
1259
      exitcode = EXIT_FAILURE;
 
1260
      goto end;
 
1261
    }
 
1262
  
 
1263
    if(quit_now){
 
1264
      goto end;
 
1265
    }
 
1266
    
 
1267
    port = (uint16_t)tmpmax;
 
1268
    *address = '\0';
 
1269
    address = connect_to;
 
1270
    /* Colon in address indicates IPv6 */
 
1271
    int af;
 
1272
    if(strchr(address, ':') != NULL){
 
1273
      af = AF_INET6;
 
1274
    } else {
 
1275
      af = AF_INET;
 
1276
    }
 
1277
    
 
1278
    if(quit_now){
 
1279
      goto end;
 
1280
    }
 
1281
    
 
1282
    ret = start_mandos_communication(address, port, if_index, af);
 
1283
    if(ret < 0){
 
1284
      exitcode = EXIT_FAILURE;
 
1285
    } else {
 
1286
      exitcode = EXIT_SUCCESS;
 
1287
    }
 
1288
    goto end;
 
1289
  }
 
1290
  
 
1291
  if(quit_now){
 
1292
    goto end;
 
1293
  }
 
1294
  
 
1295
  {
554
1296
    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 */
 
1297
    /* Do not publish any local Zeroconf records */
601
1298
    avahi_server_config_init(&config);
602
1299
    config.publish_hinfo = 0;
603
1300
    config.publish_addresses = 0;
604
1301
    config.publish_workstation = 0;
605
1302
    config.publish_domain = 0;
606
 
 
 
1303
    
607
1304
    /* 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 */
 
1305
    mc.server = avahi_server_new(avahi_simple_poll_get
 
1306
                                 (mc.simple_poll), &config, NULL,
 
1307
                                 NULL, &error);
 
1308
    
 
1309
    /* Free the Avahi configuration data */
611
1310
    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;
 
1311
  }
 
1312
  
 
1313
  /* Check if creating the Avahi server object succeeded */
 
1314
  if(mc.server == NULL){
 
1315
    fprintf(stderr, "Failed to create Avahi server: %s\n",
 
1316
            avahi_strerror(error));
 
1317
    exitcode = EXIT_FAILURE;
 
1318
    goto end;
 
1319
  }
 
1320
  
 
1321
  if(quit_now){
 
1322
    goto end;
 
1323
  }
 
1324
  
 
1325
  /* Create the Avahi service browser */
 
1326
  sb = avahi_s_service_browser_new(mc.server, if_index,
 
1327
                                   AVAHI_PROTO_UNSPEC, "_mandos._tcp",
 
1328
                                   NULL, 0, browse_callback, NULL);
 
1329
  if(sb == NULL){
 
1330
    fprintf(stderr, "Failed to create service browser: %s\n",
 
1331
            avahi_strerror(avahi_server_errno(mc.server)));
 
1332
    exitcode = EXIT_FAILURE;
 
1333
    goto end;
 
1334
  }
 
1335
  
 
1336
  if(quit_now){
 
1337
    goto end;
 
1338
  }
 
1339
  
 
1340
  /* Run the main loop */
 
1341
  
 
1342
  if(debug){
 
1343
    fprintf(stderr, "Starting Avahi loop search\n");
 
1344
  }
 
1345
  
 
1346
  avahi_simple_poll_loop(mc.simple_poll);
 
1347
  
 
1348
 end:
 
1349
  
 
1350
  if(debug){
 
1351
    fprintf(stderr, "%s exiting\n", argv[0]);
 
1352
  }
 
1353
  
 
1354
  /* Cleanup things */
 
1355
  if(sb != NULL)
 
1356
    avahi_s_service_browser_free(sb);
 
1357
  
 
1358
  if(mc.server != NULL)
 
1359
    avahi_server_free(mc.server);
 
1360
  
 
1361
  if(mc.simple_poll != NULL)
 
1362
    avahi_simple_poll_free(mc.simple_poll);
 
1363
  
 
1364
  if(gnutls_initialized){
 
1365
    gnutls_certificate_free_credentials(mc.cred);
 
1366
    gnutls_global_deinit();
 
1367
    gnutls_dh_params_deinit(mc.dh_params);
 
1368
  }
 
1369
  
 
1370
  if(gpgme_initialized){
 
1371
    gpgme_release(mc.ctx);
 
1372
  }
 
1373
  
 
1374
  /* Take down the network interface */
 
1375
  if(take_down_interface){
 
1376
    ret = ioctl(sd, SIOCGIFFLAGS, &network);
 
1377
    if(ret == -1){
 
1378
      perror("ioctl SIOCGIFFLAGS");
 
1379
    } else if(network.ifr_flags & IFF_UP) {
 
1380
      network.ifr_flags &= ~IFF_UP; /* clear flag */
 
1381
      ret = ioctl(sd, SIOCSIFFLAGS, &network);
 
1382
      if(ret == -1){
 
1383
        perror("ioctl SIOCSIFFLAGS");
 
1384
      }
 
1385
    }
 
1386
    ret = (int)TEMP_FAILURE_RETRY(close(sd));
 
1387
    if(ret == -1){
 
1388
      perror("close");
 
1389
    }
 
1390
  }
 
1391
  
 
1392
  /* Removes the temp directory used by GPGME */
 
1393
  if(tempdir_created){
 
1394
    DIR *d;
 
1395
    struct dirent *direntry;
 
1396
    d = opendir(tempdir);
 
1397
    if(d == NULL){
 
1398
      if(errno != ENOENT){
 
1399
        perror("opendir");
 
1400
      }
 
1401
    } else {
 
1402
      while(true){
 
1403
        direntry = readdir(d);
 
1404
        if(direntry == NULL){
 
1405
          break;
 
1406
        }
 
1407
        /* Skip "." and ".." */
 
1408
        if(direntry->d_name[0] == '.'
 
1409
           and (direntry->d_name[1] == '\0'
 
1410
                or (direntry->d_name[1] == '.'
 
1411
                    and direntry->d_name[2] == '\0'))){
 
1412
          continue;
 
1413
        }
 
1414
        char *fullname = NULL;
 
1415
        ret = asprintf(&fullname, "%s/%s", tempdir,
 
1416
                       direntry->d_name);
 
1417
        if(ret < 0){
 
1418
          perror("asprintf");
 
1419
          continue;
 
1420
        }
 
1421
        ret = remove(fullname);
 
1422
        if(ret == -1){
 
1423
          fprintf(stderr, "remove(\"%s\"): %s\n", fullname,
 
1424
                  strerror(errno));
 
1425
        }
 
1426
        free(fullname);
 
1427
      }
 
1428
      closedir(d);
 
1429
    }
 
1430
    ret = rmdir(tempdir);
 
1431
    if(ret == -1 and errno != ENOENT){
 
1432
      perror("rmdir");
 
1433
    }
 
1434
  }
 
1435
  
 
1436
  if(quit_now){
 
1437
    ret = sigaction(signal_received, &old_sigterm_action, NULL);
 
1438
    if(ret == -1){
 
1439
      perror("sigaction");
 
1440
    }
 
1441
    raise(signal_received);
 
1442
  }
 
1443
  
 
1444
  return exitcode;
652
1445
}