/mandos/trunk

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/trunk
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
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 Copyright © 2007-2008 Teddy Hogeborn and Björn
12
 * Påhlsson.
13
 * 
14
 * This program is free software: you can redistribute it and/or
15
 * modify it under the terms of the GNU General Public License as
16
 * published by the Free Software Foundation, either version 3 of the
17
 * License, or (at your option) any later version.
18
 * 
19
 * This program is distributed in the hope that it will be useful, but
20
 * WITHOUT ANY WARRANTY; without even the implied warranty of
21
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
22
 * General Public License for more details.
23
 * 
24
 * You should have received a copy of the GNU General Public License
25
 * along with this program.  If not, see
26
 * <http://www.gnu.org/licenses/>.
27
 * 
28
 * Contact the authors at <https://www.fukt.bsnet.se/~belorn/> and
29
 * <https://www.fukt.bsnet.se/~teddy/>.
30
 */
31
32
#define _FORTIFY_SOURCE 2
13 by Björn Påhlsson
Added following support:
33
34
#define _LARGEFILE_SOURCE
35
#define _FILE_OFFSET_BITS 64
36
37
#include <stdio.h>
38
#include <assert.h>
39
#include <stdlib.h>
40
#include <time.h>
41
#include <net/if.h>		/* if_nametoindex */
24.1.6 by Björn Påhlsson
plugbasedclient
42
#include <sys/ioctl.h> 		// ioctl, ifreq, SIOCGIFFLAGS, IFF_UP, SIOCSIFFLAGS
43
#include <net/if.h> 		// ioctl, ifreq, SIOCGIFFLAGS, IFF_UP, SIOCSIFFLAGS
13 by Björn Påhlsson
Added following support:
44
45
#include <avahi-core/core.h>
46
#include <avahi-core/lookup.h>
47
#include <avahi-core/log.h>
48
#include <avahi-common/simple-watch.h>
49
#include <avahi-common/malloc.h>
50
#include <avahi-common/error.h>
51
52
//mandos client part
22 by Teddy Hogeborn
* plugins.d/mandosclient.c (pgp_packet_decrypt): Cast "0" argument to
53
#include <sys/types.h>		/* socket(), inet_pton() */
54
#include <sys/socket.h>		/* socket(), struct sockaddr_in6,
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
55
				   struct in6_addr, inet_pton() */
56
#include <gnutls/gnutls.h>	/* All GnuTLS stuff */
57
#include <gnutls/openpgp.h>	/* GnuTLS with openpgp stuff */
13 by Björn Påhlsson
Added following support:
58
59
#include <unistd.h>		/* close() */
60
#include <netinet/in.h>
61
#include <stdbool.h>		/* true */
62
#include <string.h>		/* memset */
63
#include <arpa/inet.h>		/* inet_pton() */
64
#include <iso646.h>		/* not */
65
66
// gpgme
67
#include <errno.h>		/* perror() */
68
#include <gpgme.h>
69
15.1.3 by Björn Påhlsson
Added getopt_long support for mandosclient and passprompt
70
// getopt long
71
#include <getopt.h>
13 by Björn Påhlsson
Added following support:
72
73
#define BUFFER_SIZE 256
74
#define DH_BITS 1024
75
24.1.4 by Björn Påhlsson
Added optional parameters certdir, certkey and certfile that can be iven at start in the command line.
76
const char *certdir = "/conf/conf.d/cryptkeyreq/";
77
const char *certfile = "openpgp-client.txt";
78
const char *certkey = "openpgp-client-key.txt";
79
15.1.2 by Björn Påhlsson
Added debug options from passprompt as --debug and --debug=passprompt
80
bool debug = false;
15.1.1 by Björn Påhlsson
Added debugg support in form off --debug and --debug=mandosclient
81
13 by Björn Påhlsson
Added following support:
82
typedef struct {
83
  gnutls_session_t session;
84
  gnutls_certificate_credentials_t cred;
85
  gnutls_dh_params_t dh_params;
86
} encrypted_session;
87
88
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
89
ssize_t pgp_packet_decrypt (char *packet, size_t packet_size,
90
			    char **new_packet, const char *homedir){
13 by Björn Påhlsson
Added following support:
91
  gpgme_data_t dh_crypto, dh_plain;
92
  gpgme_ctx_t ctx;
93
  gpgme_error_t rc;
94
  ssize_t ret;
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
95
  ssize_t new_packet_capacity = 0;
96
  ssize_t new_packet_length = 0;
13 by Björn Påhlsson
Added following support:
97
  gpgme_engine_info_t engine_info;
98
15.1.1 by Björn Påhlsson
Added debugg support in form off --debug and --debug=mandosclient
99
  if (debug){
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
100
    fprintf(stderr, "Trying to decrypt OpenPGP packet\n");
15.1.1 by Björn Påhlsson
Added debugg support in form off --debug and --debug=mandosclient
101
  }
102
  
13 by Björn Påhlsson
Added following support:
103
  /* Init GPGME */
104
  gpgme_check_version(NULL);
24.1.4 by Björn Påhlsson
Added optional parameters certdir, certkey and certfile that can be iven at start in the command line.
105
  rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
106
  if (rc != GPG_ERR_NO_ERROR){
107
    fprintf(stderr, "bad gpgme_engine_check_version: %s: %s\n",
108
	    gpgme_strsource(rc), gpgme_strerror(rc));
109
    return -1;
110
  }
13 by Björn Påhlsson
Added following support:
111
  
112
  /* Set GPGME home directory */
113
  rc = gpgme_get_engine_info (&engine_info);
114
  if (rc != GPG_ERR_NO_ERROR){
115
    fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
116
	    gpgme_strsource(rc), gpgme_strerror(rc));
117
    return -1;
118
  }
119
  while(engine_info != NULL){
120
    if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
121
      gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
122
			    engine_info->file_name, homedir);
123
      break;
124
    }
125
    engine_info = engine_info->next;
126
  }
127
  if(engine_info == NULL){
128
    fprintf(stderr, "Could not set home dir to %s\n", homedir);
129
    return -1;
130
  }
131
  
132
  /* Create new GPGME data buffer from packet buffer */
133
  rc = gpgme_data_new_from_mem(&dh_crypto, packet, packet_size, 0);
134
  if (rc != GPG_ERR_NO_ERROR){
135
    fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
136
	    gpgme_strsource(rc), gpgme_strerror(rc));
137
    return -1;
138
  }
139
  
140
  /* Create new empty GPGME data buffer for the plaintext */
141
  rc = gpgme_data_new(&dh_plain);
142
  if (rc != GPG_ERR_NO_ERROR){
143
    fprintf(stderr, "bad gpgme_data_new: %s: %s\n",
144
	    gpgme_strsource(rc), gpgme_strerror(rc));
145
    return -1;
146
  }
147
  
148
  /* Create new GPGME "context" */
149
  rc = gpgme_new(&ctx);
150
  if (rc != GPG_ERR_NO_ERROR){
151
    fprintf(stderr, "bad gpgme_new: %s: %s\n",
152
	    gpgme_strsource(rc), gpgme_strerror(rc));
153
    return -1;
154
  }
155
  
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
156
  /* Decrypt data from the FILE pointer to the plaintext data
157
     buffer */
13 by Björn Påhlsson
Added following support:
158
  rc = gpgme_op_decrypt(ctx, dh_crypto, dh_plain);
159
  if (rc != GPG_ERR_NO_ERROR){
160
    fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
161
	    gpgme_strsource(rc), gpgme_strerror(rc));
162
    return -1;
163
  }
15.1.1 by Björn Påhlsson
Added debugg support in form off --debug and --debug=mandosclient
164
165
  if(debug){
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
166
    fprintf(stderr, "Decryption of OpenPGP packet succeeded\n");
15.1.1 by Björn Påhlsson
Added debugg support in form off --debug and --debug=mandosclient
167
  }
168
169
  if (debug){
170
    gpgme_decrypt_result_t result;
171
    result = gpgme_op_decrypt_result(ctx);
172
    if (result == NULL){
173
      fprintf(stderr, "gpgme_op_decrypt_result failed\n");
174
    } else {
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
175
      fprintf(stderr, "Unsupported algorithm: %s\n",
176
	      result->unsupported_algorithm);
177
      fprintf(stderr, "Wrong key usage: %d\n",
178
	      result->wrong_key_usage);
15.1.1 by Björn Påhlsson
Added debugg support in form off --debug and --debug=mandosclient
179
      if(result->file_name != NULL){
180
	fprintf(stderr, "File name: %s\n", result->file_name);
181
      }
182
      gpgme_recipient_t recipient;
183
      recipient = result->recipients;
184
      if(recipient){
185
	while(recipient != NULL){
186
	  fprintf(stderr, "Public key algorithm: %s\n",
187
		  gpgme_pubkey_algo_name(recipient->pubkey_algo));
188
	  fprintf(stderr, "Key ID: %s\n", recipient->keyid);
189
	  fprintf(stderr, "Secret key available: %s\n",
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
190
		  recipient->status == GPG_ERR_NO_SECKEY
191
		  ? "No" : "Yes");
15.1.1 by Björn Påhlsson
Added debugg support in form off --debug and --debug=mandosclient
192
	  recipient = recipient->next;
193
	}
194
      }
195
    }
196
  }
13 by Björn Påhlsson
Added following support:
197
  
198
  /* Delete the GPGME FILE pointer cryptotext data buffer */
199
  gpgme_data_release(dh_crypto);
200
  
201
  /* Seek back to the beginning of the GPGME plaintext data buffer */
24.1.5 by Björn Påhlsson
plugbasedclient:
202
  if (gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET) == -1){
203
    perror("pgpme_data_seek");
204
  }
205
  
13 by Björn Påhlsson
Added following support:
206
  *new_packet = 0;
207
  while(true){
208
    if (new_packet_length + BUFFER_SIZE > new_packet_capacity){
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
209
      *new_packet = realloc(*new_packet,
210
			    (unsigned int)new_packet_capacity
211
			    + BUFFER_SIZE);
13 by Björn Påhlsson
Added following support:
212
      if (*new_packet == NULL){
213
	perror("realloc");
214
	return -1;
215
      }
216
      new_packet_capacity += BUFFER_SIZE;
217
    }
218
    
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
219
    ret = gpgme_data_read(dh_plain, *new_packet + new_packet_length,
220
			  BUFFER_SIZE);
13 by Björn Påhlsson
Added following support:
221
    /* Print the data, if any */
222
    if (ret == 0){
223
      break;
224
    }
225
    if(ret < 0){
226
      perror("gpgme_data_read");
227
      return -1;
228
    }
229
    new_packet_length += ret;
230
  }
231
15.1.3 by Björn Påhlsson
Added getopt_long support for mandosclient and passprompt
232
  /* FIXME: check characters before printing to screen so to not print
233
     terminal control characters */
234
  /*   if(debug){ */
235
  /*     fprintf(stderr, "decrypted password is: "); */
236
  /*     fwrite(*new_packet, 1, new_packet_length, stderr); */
237
  /*     fprintf(stderr, "\n"); */
238
  /*   } */
239
  
240
  /* Delete the GPGME plaintext data buffer */
13 by Björn Påhlsson
Added following support:
241
  gpgme_data_release(dh_plain);
242
  return new_packet_length;
243
}
244
245
static const char * safer_gnutls_strerror (int value) {
246
  const char *ret = gnutls_strerror (value);
247
  if (ret == NULL)
248
    ret = "(unknown)";
249
  return ret;
250
}
251
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
252
void debuggnutls(__attribute__((unused)) int level,
253
		 const char* string){
13 by Björn Påhlsson
Added following support:
254
  fprintf(stderr, "%s", string);
255
}
256
257
int initgnutls(encrypted_session *es){
258
  const char *err;
259
  int ret;
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
260
  
15.1.1 by Björn Påhlsson
Added debugg support in form off --debug and --debug=mandosclient
261
  if(debug){
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
262
    fprintf(stderr, "Initializing GnuTLS\n");
15.1.1 by Björn Påhlsson
Added debugg support in form off --debug and --debug=mandosclient
263
  }
24.1.4 by Björn Påhlsson
Added optional parameters certdir, certkey and certfile that can be iven at start in the command line.
264
13 by Björn Påhlsson
Added following support:
265
  if ((ret = gnutls_global_init ())
266
      != GNUTLS_E_SUCCESS) {
267
    fprintf (stderr, "global_init: %s\n", safer_gnutls_strerror(ret));
268
    return -1;
269
  }
270
15.1.1 by Björn Påhlsson
Added debugg support in form off --debug and --debug=mandosclient
271
  if (debug){
272
    gnutls_global_set_log_level(11);
273
    gnutls_global_set_log_function(debuggnutls);
274
  }
275
  
13 by Björn Påhlsson
Added following support:
276
  /* openpgp credentials */
277
  if ((ret = gnutls_certificate_allocate_credentials (&es->cred))
278
      != GNUTLS_E_SUCCESS) {
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
279
    fprintf (stderr, "memory error: %s\n",
280
	     safer_gnutls_strerror(ret));
13 by Björn Påhlsson
Added following support:
281
    return -1;
282
  }
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
283
  
15.1.1 by Björn Påhlsson
Added debugg support in form off --debug and --debug=mandosclient
284
  if(debug){
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
285
    fprintf(stderr, "Attempting to use OpenPGP certificate %s"
24.1.4 by Björn Påhlsson
Added optional parameters certdir, certkey and certfile that can be iven at start in the command line.
286
	    " and keyfile %s as GnuTLS credentials\n", certfile,
287
	    certkey);
15.1.1 by Björn Påhlsson
Added debugg support in form off --debug and --debug=mandosclient
288
  }
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
289
  
13 by Björn Påhlsson
Added following support:
290
  ret = gnutls_certificate_set_openpgp_key_file
24.1.4 by Björn Påhlsson
Added optional parameters certdir, certkey and certfile that can be iven at start in the command line.
291
    (es->cred, certfile, certkey, GNUTLS_OPENPGP_FMT_BASE64);
13 by Björn Påhlsson
Added following support:
292
  if (ret != GNUTLS_E_SUCCESS) {
293
    fprintf
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
294
      (stderr, "Error[%d] while reading the OpenPGP key pair ('%s',"
295
       " '%s')\n",
24.1.4 by Björn Påhlsson
Added optional parameters certdir, certkey and certfile that can be iven at start in the command line.
296
       ret, certfile, certkey);
13 by Björn Påhlsson
Added following support:
297
    fprintf(stdout, "The Error is: %s\n",
298
	    safer_gnutls_strerror(ret));
299
    return -1;
300
  }
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
301
  
302
  //GnuTLS server initialization
13 by Björn Påhlsson
Added following support:
303
  if ((ret = gnutls_dh_params_init (&es->dh_params))
304
      != GNUTLS_E_SUCCESS) {
305
    fprintf (stderr, "Error in dh parameter initialization: %s\n",
306
	     safer_gnutls_strerror(ret));
307
    return -1;
308
  }
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
309
  
13 by Björn Påhlsson
Added following support:
310
  if ((ret = gnutls_dh_params_generate2 (es->dh_params, DH_BITS))
311
      != GNUTLS_E_SUCCESS) {
312
    fprintf (stderr, "Error in prime generation: %s\n",
313
	     safer_gnutls_strerror(ret));
314
    return -1;
315
  }
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
316
  
13 by Björn Påhlsson
Added following support:
317
  gnutls_certificate_set_dh_params (es->cred, es->dh_params);
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
318
  
319
  // GnuTLS session creation
13 by Björn Påhlsson
Added following support:
320
  if ((ret = gnutls_init (&es->session, GNUTLS_SERVER))
321
      != GNUTLS_E_SUCCESS){
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
322
    fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
13 by Björn Påhlsson
Added following support:
323
	    safer_gnutls_strerror(ret));
324
  }
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
325
  
13 by Björn Påhlsson
Added following support:
326
  if ((ret = gnutls_priority_set_direct (es->session, "NORMAL", &err))
327
      != GNUTLS_E_SUCCESS) {
328
    fprintf(stderr, "Syntax error at: %s\n", err);
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
329
    fprintf(stderr, "GnuTLS error: %s\n",
13 by Björn Påhlsson
Added following support:
330
	    safer_gnutls_strerror(ret));
331
    return -1;
332
  }
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
333
  
13 by Björn Påhlsson
Added following support:
334
  if ((ret = gnutls_credentials_set
335
       (es->session, GNUTLS_CRD_CERTIFICATE, es->cred))
336
      != GNUTLS_E_SUCCESS) {
337
    fprintf(stderr, "Error setting a credentials set: %s\n",
338
	    safer_gnutls_strerror(ret));
339
    return -1;
340
  }
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
341
  
13 by Björn Påhlsson
Added following support:
342
  /* ignore client certificate if any. */
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
343
  gnutls_certificate_server_set_request (es->session,
344
					 GNUTLS_CERT_IGNORE);
13 by Björn Påhlsson
Added following support:
345
  
346
  gnutls_dh_set_prime_bits (es->session, DH_BITS);
347
  
348
  return 0;
349
}
350
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
351
void empty_log(__attribute__((unused)) AvahiLogLevel level,
352
	       __attribute__((unused)) const char *txt){}
13 by Björn Påhlsson
Added following support:
353
22 by Teddy Hogeborn
* plugins.d/mandosclient.c (pgp_packet_decrypt): Cast "0" argument to
354
int start_mandos_communication(const char *ip, uint16_t port,
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
355
			       unsigned int if_index){
13 by Björn Påhlsson
Added following support:
356
  int ret, tcp_sd;
357
  struct sockaddr_in6 to;
358
  encrypted_session es;
359
  char *buffer = NULL;
360
  char *decrypted_buffer;
361
  size_t buffer_length = 0;
362
  size_t buffer_capacity = 0;
363
  ssize_t decrypted_buffer_size;
22 by Teddy Hogeborn
* plugins.d/mandosclient.c (pgp_packet_decrypt): Cast "0" argument to
364
  size_t written = 0;
13 by Björn Påhlsson
Added following support:
365
  int retval = 0;
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
366
  char interface[IF_NAMESIZE];
367
  
15.1.1 by Björn Påhlsson
Added debugg support in form off --debug and --debug=mandosclient
368
  if(debug){
369
    fprintf(stderr, "Setting up a tcp connection to %s\n", ip);
370
  }
13 by Björn Påhlsson
Added following support:
371
  
372
  tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
373
  if(tcp_sd < 0) {
374
    perror("socket");
375
    return -1;
376
  }
24.1.6 by Björn Påhlsson
plugbasedclient
377
378
  if(debug){
379
    if(if_indextoname(if_index, interface) == NULL){
380
      if(debug){
381
	perror("if_indextoname");
382
      }
383
      return -1;
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
384
    }
24.1.6 by Björn Påhlsson
plugbasedclient
385
    
15.1.1 by Björn Påhlsson
Added debugg support in form off --debug and --debug=mandosclient
386
    fprintf(stderr, "Binding to interface %s\n", interface);
387
  }
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
388
  
22 by Teddy Hogeborn
* plugins.d/mandosclient.c (pgp_packet_decrypt): Cast "0" argument to
389
  memset(&to,0,sizeof(to));	/* Spurious warning */
13 by Björn Påhlsson
Added following support:
390
  to.sin6_family = AF_INET6;
18 by Teddy Hogeborn
* plugins.d/Makefile: Removed
391
  ret = inet_pton(AF_INET6, ip, &to.sin6_addr);
13 by Björn Påhlsson
Added following support:
392
  if (ret < 0 ){
393
    perror("inet_pton");
394
    return -1;
395
  }  
396
  if(ret == 0){
397
    fprintf(stderr, "Bad address: %s\n", ip);
398
    return -1;
399
  }
22 by Teddy Hogeborn
* plugins.d/mandosclient.c (pgp_packet_decrypt): Cast "0" argument to
400
  to.sin6_port = htons(port);	/* Spurious warning */
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
401
  
402
  to.sin6_scope_id = (uint32_t)if_index;
403
  
15.1.1 by Björn Påhlsson
Added debugg support in form off --debug and --debug=mandosclient
404
  if(debug){
405
    fprintf(stderr, "Connection to: %s\n", ip);
406
  }
13 by Björn Påhlsson
Added following support:
407
  
408
  ret = connect(tcp_sd, (struct sockaddr *) &to, sizeof(to));
409
  if (ret < 0){
410
    perror("connect");
411
    return -1;
412
  }
413
  
414
  ret = initgnutls (&es);
415
  if (ret != 0){
416
    retval = -1;
417
    return -1;
418
  }
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
419
  
420
  gnutls_transport_set_ptr (es.session,
421
			    (gnutls_transport_ptr_t) tcp_sd);
422
  
15.1.1 by Björn Påhlsson
Added debugg support in form off --debug and --debug=mandosclient
423
  if(debug){
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
424
    fprintf(stderr, "Establishing TLS session with %s\n", ip);
15.1.1 by Björn Påhlsson
Added debugg support in form off --debug and --debug=mandosclient
425
  }
426
  
13 by Björn Påhlsson
Added following support:
427
  ret = gnutls_handshake (es.session);
428
  
429
  if (ret != GNUTLS_E_SUCCESS){
25 by Teddy Hogeborn
* mandos-clients.conf ([DEFAULT]): New section.
430
    if(debug){
431
      fprintf(stderr, "\n*** Handshake failed ***\n");
432
      gnutls_perror (ret);
433
    }
13 by Björn Påhlsson
Added following support:
434
    retval = -1;
435
    goto exit;
436
  }
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
437
  
438
  //Retrieve OpenPGP packet that contains the wanted password
439
  
15.1.1 by Björn Påhlsson
Added debugg support in form off --debug and --debug=mandosclient
440
  if(debug){
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
441
    fprintf(stderr, "Retrieving pgp encrypted password from %s\n",
442
	    ip);
15.1.1 by Björn Påhlsson
Added debugg support in form off --debug and --debug=mandosclient
443
  }
444
13 by Björn Påhlsson
Added following support:
445
  while(true){
446
    if (buffer_length + BUFFER_SIZE > buffer_capacity){
447
      buffer = realloc(buffer, buffer_capacity + BUFFER_SIZE);
448
      if (buffer == NULL){
449
	perror("realloc");
450
	goto exit;
451
      }
452
      buffer_capacity += BUFFER_SIZE;
453
    }
454
    
455
    ret = gnutls_record_recv
456
      (es.session, buffer+buffer_length, BUFFER_SIZE);
457
    if (ret == 0){
458
      break;
459
    }
460
    if (ret < 0){
461
      switch(ret){
462
      case GNUTLS_E_INTERRUPTED:
463
      case GNUTLS_E_AGAIN:
464
	break;
465
      case GNUTLS_E_REHANDSHAKE:
466
	ret = gnutls_handshake (es.session);
467
	if (ret < 0){
468
	  fprintf(stderr, "\n*** Handshake failed ***\n");
469
	  gnutls_perror (ret);
470
	  retval = -1;
471
	  goto exit;
472
	}
473
	break;
474
      default:
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
475
	fprintf(stderr, "Unknown error while reading data from"
476
		" encrypted session with mandos server\n");
13 by Björn Påhlsson
Added following support:
477
	retval = -1;
478
	gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
479
	goto exit;
480
      }
481
    } else {
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
482
      buffer_length += (size_t) ret;
13 by Björn Påhlsson
Added following support:
483
    }
484
  }
15.1.1 by Björn Påhlsson
Added debugg support in form off --debug and --debug=mandosclient
485
  
13 by Björn Påhlsson
Added following support:
486
  if (buffer_length > 0){
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
487
    decrypted_buffer_size = pgp_packet_decrypt(buffer,
488
					       buffer_length,
489
					       &decrypted_buffer,
24.1.4 by Björn Påhlsson
Added optional parameters certdir, certkey and certfile that can be iven at start in the command line.
490
					       certdir);
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
491
    if (decrypted_buffer_size >= 0){
24.1.6 by Björn Påhlsson
plugbasedclient
492
      while(written < (size_t)decrypted_buffer_size){
22 by Teddy Hogeborn
* plugins.d/mandosclient.c (pgp_packet_decrypt): Cast "0" argument to
493
	ret = (int)fwrite (decrypted_buffer + written, 1,
494
			   (size_t)decrypted_buffer_size - written,
495
			   stdout);
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
496
	if(ret == 0 and ferror(stdout)){
497
	  if(debug){
498
	    fprintf(stderr, "Error writing encrypted data: %s\n",
499
		    strerror(errno));
500
	  }
501
	  retval = -1;
502
	  break;
503
	}
22 by Teddy Hogeborn
* plugins.d/mandosclient.c (pgp_packet_decrypt): Cast "0" argument to
504
	written += (size_t)ret;
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
505
      }
13 by Björn Påhlsson
Added following support:
506
      free(decrypted_buffer);
15.1.1 by Björn Påhlsson
Added debugg support in form off --debug and --debug=mandosclient
507
    } else {
508
      retval = -1;
13 by Björn Påhlsson
Added following support:
509
    }
510
  }
511
15.1.1 by Björn Påhlsson
Added debugg support in form off --debug and --debug=mandosclient
512
  //shutdown procedure
513
514
  if(debug){
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
515
    fprintf(stderr, "Closing TLS session\n");
15.1.1 by Björn Påhlsson
Added debugg support in form off --debug and --debug=mandosclient
516
  }
517
13 by Björn Påhlsson
Added following support:
518
  free(buffer);
519
  gnutls_bye (es.session, GNUTLS_SHUT_RDWR);
520
 exit:
521
  close(tcp_sd);
522
  gnutls_deinit (es.session);
523
  gnutls_certificate_free_credentials (es.cred);
524
  gnutls_global_deinit ();
525
  return retval;
526
}
527
528
static AvahiSimplePoll *simple_poll = NULL;
529
static AvahiServer *server = NULL;
530
531
static void resolve_callback(
532
    AvahiSServiceResolver *r,
22 by Teddy Hogeborn
* plugins.d/mandosclient.c (pgp_packet_decrypt): Cast "0" argument to
533
    AvahiIfIndex interface,
13 by Björn Påhlsson
Added following support:
534
    AVAHI_GCC_UNUSED AvahiProtocol protocol,
535
    AvahiResolverEvent event,
536
    const char *name,
537
    const char *type,
538
    const char *domain,
539
    const char *host_name,
540
    const AvahiAddress *address,
541
    uint16_t port,
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
542
    AVAHI_GCC_UNUSED AvahiStringList *txt,
543
    AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
13 by Björn Påhlsson
Added following support:
544
    AVAHI_GCC_UNUSED void* userdata) {
545
    
22 by Teddy Hogeborn
* plugins.d/mandosclient.c (pgp_packet_decrypt): Cast "0" argument to
546
  assert(r);			/* Spurious warning */
547
  
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
548
  /* Called whenever a service has been resolved successfully or
549
     timed out */
22 by Teddy Hogeborn
* plugins.d/mandosclient.c (pgp_packet_decrypt): Cast "0" argument to
550
  
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
551
  switch (event) {
552
  default:
553
  case AVAHI_RESOLVER_FAILURE:
554
    fprintf(stderr, "(Resolver) Failed to resolve service '%s' of"
555
	    " type '%s' in domain '%s': %s\n", name, type, domain,
556
	    avahi_strerror(avahi_server_errno(server)));
557
    break;
22 by Teddy Hogeborn
* plugins.d/mandosclient.c (pgp_packet_decrypt): Cast "0" argument to
558
    
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
559
  case AVAHI_RESOLVER_FOUND:
560
    {
561
      char ip[AVAHI_ADDRESS_STR_MAX];
562
      avahi_address_snprint(ip, sizeof(ip), address);
563
      if(debug){
25 by Teddy Hogeborn
* mandos-clients.conf ([DEFAULT]): New section.
564
	fprintf(stderr, "Mandos server \"%s\" found on %s (%s) on"
565
		" port %d\n", name, host_name, ip, port);
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
566
      }
567
      int ret = start_mandos_communication(ip, port,
22 by Teddy Hogeborn
* plugins.d/mandosclient.c (pgp_packet_decrypt): Cast "0" argument to
568
					   (unsigned int) interface);
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
569
      if (ret == 0){
570
	exit(EXIT_SUCCESS);
571
      }
13 by Björn Påhlsson
Added following support:
572
    }
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
573
  }
574
  avahi_s_service_resolver_free(r);
13 by Björn Påhlsson
Added following support:
575
}
576
577
static void browse_callback(
578
    AvahiSServiceBrowser *b,
579
    AvahiIfIndex interface,
580
    AvahiProtocol protocol,
581
    AvahiBrowserEvent event,
582
    const char *name,
583
    const char *type,
584
    const char *domain,
585
    AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
586
    void* userdata) {
587
    
588
    AvahiServer *s = userdata;
22 by Teddy Hogeborn
* plugins.d/mandosclient.c (pgp_packet_decrypt): Cast "0" argument to
589
    assert(b);			/* Spurious warning */
590
    
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
591
    /* Called whenever a new services becomes available on the LAN or
592
       is removed from the LAN */
22 by Teddy Hogeborn
* plugins.d/mandosclient.c (pgp_packet_decrypt): Cast "0" argument to
593
    
13 by Björn Påhlsson
Added following support:
594
    switch (event) {
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
595
    default:
596
    case AVAHI_BROWSER_FAILURE:
597
      
598
      fprintf(stderr, "(Browser) %s\n",
599
	      avahi_strerror(avahi_server_errno(server)));
600
      avahi_simple_poll_quit(simple_poll);
601
      return;
602
      
603
    case AVAHI_BROWSER_NEW:
604
      /* We ignore the returned resolver object. In the callback
605
	 function we free it. If the server is terminated before
606
	 the callback function is called the server will free
607
	 the resolver for us. */
608
      
609
      if (!(avahi_s_service_resolver_new(s, interface, protocol, name,
610
					 type, domain,
611
					 AVAHI_PROTO_INET6, 0,
612
					 resolve_callback, s)))
613
	fprintf(stderr, "Failed to resolve service '%s': %s\n", name,
614
		avahi_strerror(avahi_server_errno(s)));
615
      break;
616
      
617
    case AVAHI_BROWSER_REMOVE:
618
      break;
619
      
620
    case AVAHI_BROWSER_ALL_FOR_NOW:
621
    case AVAHI_BROWSER_CACHE_EXHAUSTED:
622
      break;
13 by Björn Påhlsson
Added following support:
623
    }
624
}
625
24.1.5 by Björn Påhlsson
plugbasedclient:
626
/* combinds file name and path and returns the malloced new string. som sane checks could/should be added */
627
const char *combinepath(const char *first, const char *second){
24.1.4 by Björn Påhlsson
Added optional parameters certdir, certkey and certfile that can be iven at start in the command line.
628
  char *tmp;
24.1.5 by Björn Påhlsson
plugbasedclient:
629
  tmp = malloc(strlen(first) + strlen(second) + 2);
24.1.4 by Björn Påhlsson
Added optional parameters certdir, certkey and certfile that can be iven at start in the command line.
630
  if (tmp == NULL){
631
    perror("malloc");
632
    return NULL;
633
  }
634
  strcpy(tmp, first);
24.1.5 by Björn Påhlsson
plugbasedclient:
635
  if (first[0] != '\0' and first[strlen(first) - 1] != '/'){
636
    strcat(tmp, "/");
637
  }
24.1.4 by Björn Påhlsson
Added optional parameters certdir, certkey and certfile that can be iven at start in the command line.
638
  strcat(tmp, second);
639
  return tmp;
640
}
641
642
13 by Björn Påhlsson
Added following support:
643
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
644
    AvahiServerConfig config;
645
    AvahiSServiceBrowser *sb = NULL;
646
    int error;
15.1.3 by Björn Påhlsson
Added getopt_long support for mandosclient and passprompt
647
    int ret;
15.1.1 by Björn Påhlsson
Added debugg support in form off --debug and --debug=mandosclient
648
    int returncode = EXIT_SUCCESS;
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
649
    const char *interface = "eth0";
24.1.6 by Björn Påhlsson
plugbasedclient
650
    struct ifreq network;
651
    int sd;
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
652
    
15.1.3 by Björn Påhlsson
Added getopt_long support for mandosclient and passprompt
653
    while (true){
654
      static struct option long_options[] = {
655
	{"debug", no_argument, (int *)&debug, 1},
656
	{"interface", required_argument, 0, 'i'},
24.1.4 by Björn Påhlsson
Added optional parameters certdir, certkey and certfile that can be iven at start in the command line.
657
	{"certdir", required_argument, 0, 'd'},
658
	{"certkey", required_argument, 0, 'c'},
659
	{"certfile", required_argument, 0, 'k'},
15.1.3 by Björn Påhlsson
Added getopt_long support for mandosclient and passprompt
660
	{0, 0, 0, 0} };
22 by Teddy Hogeborn
* plugins.d/mandosclient.c (pgp_packet_decrypt): Cast "0" argument to
661
      
15.1.3 by Björn Påhlsson
Added getopt_long support for mandosclient and passprompt
662
      int option_index = 0;
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
663
      ret = getopt_long (argc, argv, "i:", long_options,
664
			 &option_index);
22 by Teddy Hogeborn
* plugins.d/mandosclient.c (pgp_packet_decrypt): Cast "0" argument to
665
      
15.1.3 by Björn Påhlsson
Added getopt_long support for mandosclient and passprompt
666
      if (ret == -1){
667
	break;
668
      }
669
      
670
      switch(ret){
671
      case 0:
672
	break;
673
      case 'i':
674
	interface = optarg;
675
	break;
24.1.4 by Björn Påhlsson
Added optional parameters certdir, certkey and certfile that can be iven at start in the command line.
676
      case 'd':
677
	certdir = optarg;
678
	break;
679
      case 'c':
680
	certfile = optarg;
681
	break;
682
      case 'k':
683
	certkey = optarg;
684
	break;
15.1.3 by Björn Påhlsson
Added getopt_long support for mandosclient and passprompt
685
      default:
686
	exit(EXIT_FAILURE);
687
      }
688
    }
24.1.4 by Björn Påhlsson
Added optional parameters certdir, certkey and certfile that can be iven at start in the command line.
689
24.1.5 by Björn Påhlsson
plugbasedclient:
690
    certfile = combinepath(certdir, certfile);
24.1.4 by Björn Påhlsson
Added optional parameters certdir, certkey and certfile that can be iven at start in the command line.
691
    if (certfile == NULL){
24.1.6 by Björn Påhlsson
plugbasedclient
692
      returncode = EXIT_FAILURE;
24.1.4 by Björn Påhlsson
Added optional parameters certdir, certkey and certfile that can be iven at start in the command line.
693
      goto exit;
694
    }
15.1.3 by Björn Påhlsson
Added getopt_long support for mandosclient and passprompt
695
    
24.1.5 by Björn Påhlsson
plugbasedclient:
696
    certkey = combinepath(certdir, certkey);
24.1.4 by Björn Påhlsson
Added optional parameters certdir, certkey and certfile that can be iven at start in the command line.
697
    if (certkey == NULL){
24.1.6 by Björn Påhlsson
plugbasedclient
698
      returncode = EXIT_FAILURE;
699
      goto exit;
700
    }
701
702
    sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
703
    if(sd < 0) {
704
      perror("socket");
705
      returncode = EXIT_FAILURE;
706
      goto exit;
707
    }
708
    strcpy(network.ifr_name, interface);    
709
    ret = ioctl(sd, SIOCGIFFLAGS, &network);
710
    if(ret == -1){
711
      
712
      perror("ioctl SIOCGIFFLAGS");
713
      returncode = EXIT_FAILURE;
714
      goto exit;
715
    }
716
    if((network.ifr_flags & IFF_UP) == 0){
717
      network.ifr_flags |= IFF_UP;
718
      ret = ioctl(sd, SIOCSIFFLAGS, &network);
719
      if(ret == -1){
720
	perror("ioctl SIOCSIFFLAGS");
721
	returncode = EXIT_FAILURE;
722
	goto exit;
723
      }
724
    }
725
    close(sd);
726
    
15.1.1 by Björn Påhlsson
Added debugg support in form off --debug and --debug=mandosclient
727
    if (not debug){
728
      avahi_set_log_function(empty_log);
729
    }
13 by Björn Påhlsson
Added following support:
730
    
731
    /* Initialize the psuedo-RNG */
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
732
    srand((unsigned int) time(NULL));
13 by Björn Påhlsson
Added following support:
733
734
    /* Allocate main loop object */
735
    if (!(simple_poll = avahi_simple_poll_new())) {
736
        fprintf(stderr, "Failed to create simple poll object.\n");
24.1.6 by Björn Påhlsson
plugbasedclient
737
	returncode = EXIT_FAILURE;	
15.1.1 by Björn Påhlsson
Added debugg support in form off --debug and --debug=mandosclient
738
        goto exit;
13 by Björn Påhlsson
Added following support:
739
    }
740
741
    /* Do not publish any local records */
742
    avahi_server_config_init(&config);
743
    config.publish_hinfo = 0;
744
    config.publish_addresses = 0;
745
    config.publish_workstation = 0;
746
    config.publish_domain = 0;
747
748
    /* Allocate a new server */
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
749
    server = avahi_server_new(avahi_simple_poll_get(simple_poll),
750
			      &config, NULL, NULL, &error);
13 by Björn Påhlsson
Added following support:
751
752
    /* Free the configuration data */
753
    avahi_server_config_free(&config);
754
15.1.1 by Björn Påhlsson
Added debugg support in form off --debug and --debug=mandosclient
755
    /* Check if creating the server object succeeded */
13 by Björn Påhlsson
Added following support:
756
    if (!server) {
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
757
        fprintf(stderr, "Failed to create server: %s\n",
758
		avahi_strerror(error));
15.1.1 by Björn Påhlsson
Added debugg support in form off --debug and --debug=mandosclient
759
	returncode = EXIT_FAILURE;
760
        goto exit;
13 by Björn Påhlsson
Added following support:
761
    }
762
    
763
    /* Create the service browser */
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
764
    sb = avahi_s_service_browser_new(server,
765
				     (AvahiIfIndex)
766
				     if_nametoindex(interface),
767
				     AVAHI_PROTO_INET6,
768
				     "_mandos._tcp", NULL, 0,
769
				     browse_callback, server);
770
    if (!sb) {
771
        fprintf(stderr, "Failed to create service browser: %s\n",
772
		avahi_strerror(avahi_server_errno(server)));
15.1.1 by Björn Påhlsson
Added debugg support in form off --debug and --debug=mandosclient
773
	returncode = EXIT_FAILURE;
774
        goto exit;
13 by Björn Påhlsson
Added following support:
775
    }
776
    
777
    /* Run the main loop */
15.1.1 by Björn Påhlsson
Added debugg support in form off --debug and --debug=mandosclient
778
779
    if (debug){
780
      fprintf(stderr, "Starting avahi loop search\n");
781
    }
782
    
13 by Björn Påhlsson
Added following support:
783
    avahi_simple_poll_loop(simple_poll);
784
    
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
785
 exit:
15.1.1 by Björn Påhlsson
Added debugg support in form off --debug and --debug=mandosclient
786
787
    if (debug){
788
      fprintf(stderr, "%s exiting\n", argv[0]);
789
    }
13 by Björn Påhlsson
Added following support:
790
    
791
    /* Cleanup things */
792
    if (sb)
793
        avahi_s_service_browser_free(sb);
794
    
795
    if (server)
796
        avahi_server_free(server);
797
798
    if (simple_poll)
799
        avahi_simple_poll_free(simple_poll);
24.1.5 by Björn Påhlsson
plugbasedclient:
800
    free(certfile);
801
    free(certkey);
802
    
15.1.3 by Björn Påhlsson
Added getopt_long support for mandosclient and passprompt
803
    return returncode;
13 by Björn Påhlsson
Added following support:
804
}