/mandos/release

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

« back to all changes in this revision

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

Merge from Björn:

* plugin-runner.c (main): Bug fix: For the "--options-for" option, do
                          not mangle arguments with colon characters.
                          Also support adding empty arguments.

* plugins.d/mandos-client.c: (mc): New global variable; moved from
                                   "main".
  (init_gpgme, pgp_packet_decrypt, init_gnutls_global,
  init_gnutls_session, start_mandos_communication): Removed "mc"
                                                    argument.  All
                                                    callers changed.

  (resolve_callback, browse_callback): Ignore "userdata" argument.
                                       All callers changed.
  (handle_sigterm): New function.
  (main): Add "handle_sigterm" as signal handler for SIGTERM before
          starting the main loop.

Show diffs side-by-side

added added

removed removed

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