summaryrefslogtreecommitdiff
path: root/lib/gnutls_cert.c
blob: e624f01fff66a9f991e8593a9922a4f71c85079d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
/*
 *      Copyright (C) 2001,2002 Nikos Mavroyanopoulos
 *
 * This file is part of GNUTLS.
 *
 * GNUTLS is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * GNUTLS is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
 */

#include <gnutls_int.h>
#include <gnutls_errors.h>
#include <auth_cert.h>
#include <gnutls_cert.h>
#include <x509_asn1.h>
#include <x509_der.h>
#include <gnutls_datum.h>
#include <gnutls_mpi.h>
#include <gnutls_global.h>
#include <x509_verify.h>
#include <gnutls_privkey.h>
#include <x509_extensions.h>
#include <gnutls_algorithms.h>
#include <gnutls_dh.h>
#include <gnutls_str.h>
#include <gnutls_state.h>
#include <gnutls_auth_int.h>
#include <gnutls_x509.h>
#include <gnutls_openpgp.h>

/* KX mappings to PK algorithms */
typedef struct {
	KXAlgorithm kx_algorithm;
	PKAlgorithm pk_algorithm;
} gnutls_pk_map;

/* This table maps the Key exchange algorithms to
 * the certificate algorithms. Eg. if we have
 * RSA algorithm in the certificate then we can
 * use GNUTLS_KX_RSA or GNUTLS_KX_DHE_RSA.
 */
static const gnutls_pk_map pk_mappings[] = {
	{GNUTLS_KX_RSA, GNUTLS_PK_RSA},
	{GNUTLS_KX_DHE_RSA, GNUTLS_PK_RSA},
	{GNUTLS_KX_DHE_DSS, GNUTLS_PK_DSA},
	{0}
};

#define GNUTLS_PK_MAP_LOOP(b) \
        const gnutls_pk_map *p; \
                for(p = pk_mappings; p->kx_algorithm != 0; p++) { b ; }

#define GNUTLS_PK_MAP_ALG_LOOP(a) \
                        GNUTLS_PK_MAP_LOOP( if(p->kx_algorithm == kx_algorithm) { a; break; })


/* returns the PKAlgorithm which is compatible with
 * the given KXAlgorithm.
 */
PKAlgorithm _gnutls_map_pk_get_pk(KXAlgorithm kx_algorithm)
{
	PKAlgorithm ret = -1;

	GNUTLS_PK_MAP_ALG_LOOP(ret = p->pk_algorithm);
	return ret;
}

void _gnutls_free_cert(gnutls_cert cert)
{
	int i;

	for (i = 0; i < cert.params_size; i++) {
		_gnutls_mpi_release(&cert.params[i]);
	}

	gnutls_free_datum(&cert.signature);
	gnutls_free_datum(&cert.raw);

	return;
}

/**
  * gnutls_certificate_free_sc - Used to free an allocated CERTIFICATE CREDENTIALS structure
  * @sc: is an &GNUTLS_CERTIFICATE_CREDENTIALS structure.
  *
  * This structure is complex enough to manipulate directly thus
  * this helper function is provided in order to free (deallocate)
  * the structure.
  **/
void gnutls_certificate_free_sc(GNUTLS_CERTIFICATE_CREDENTIALS sc)
{
	int i, j;

	for (i = 0; i < sc->ncerts; i++) {
		for (j = 0; j < sc->cert_list_length[i]; j++) {
			_gnutls_free_cert(sc->cert_list[i][j]);
		}
		gnutls_free( sc->cert_list[i]);
	}

	gnutls_free(sc->cert_list_length);
	gnutls_free(sc->cert_list);

	for (j = 0; j < sc->x509_ncas; j++) {
		_gnutls_free_cert( sc->x509_ca_list[j]);
	}

	gnutls_free( sc->x509_ca_list);
	gnutls_free_datum( &sc->keyring);

	for (i = 0; i < sc->ncerts; i++) {
		_gnutls_free_private_key(sc->pkey[i]);
	}

	gnutls_free( sc->pkey);
	gnutls_free( sc->x509_rdn_sequence.data);

	gnutls_free( sc);
}


/**
  * gnutls_certificate_allocate_sc - Used to allocate an x509 SERVER CREDENTIALS structure
  * @res: is a pointer to an &GNUTLS_CERTIFICATE_CREDENTIALS structure.
  *
  * This structure is complex enough to manipulate directly thus
  * this helper function is provided in order to allocate
  * the structure.
  **/
int gnutls_certificate_allocate_sc(GNUTLS_CERTIFICATE_CREDENTIALS * res)
{
	*res = gnutls_calloc(1, sizeof(CERTIFICATE_CREDENTIALS_INT));

	if (*res == NULL)
		return GNUTLS_E_MEMORY_ERROR;

	(*res)->dh_params = &_gnutls_dh_default_params;

	return 0;
}


/* returns the KX algorithms that are supported by a
 * certificate. (Eg a certificate with RSA params, supports
 * GNUTLS_KX_RSA algorithm).
 * This function also uses the KeyUsage field of the certificate
 * extensions in order to disable unneded algorithms.
 */
int _gnutls_cert_supported_kx(const gnutls_cert * cert, KXAlgorithm ** alg,
			      int *alg_size)
{
	KXAlgorithm kx;
	int i;
	PKAlgorithm pk;
	KXAlgorithm kxlist[MAX_ALGOS];

	i = 0;
	for (kx = 0; kx < MAX_ALGOS; kx++) {
		pk = _gnutls_map_pk_get_pk(kx);
		if (pk == cert->subject_pk_algorithm) {
			if (cert->cert_type==GNUTLS_CRT_X509) {
				/* then check key usage */
				if (_gnutls_check_x509_key_usage(cert, kx) == 0) {
					kxlist[i] = kx;
					i++;
				}
			} else if ( cert->cert_type==GNUTLS_CRT_OPENPGP) {
				/* FIXME: something like key usage
				 * should be added
				 */
				kxlist[i] = kx;
				i++;
			}
		}
	}

	if (i==0) {
		gnutls_assert();
		return GNUTLS_E_INVALID_PARAMETERS;
	}

	*alg = gnutls_calloc(1, sizeof(KXAlgorithm) * i);
	if (*alg == NULL)
		return GNUTLS_E_MEMORY_ERROR;

	*alg_size = i;

	memcpy(*alg, kxlist, i * sizeof(KXAlgorithm));

	return 0;
}


/**
  * gnutls_certificate_server_set_request - Used to set whether to request a client certificate
  * @state: is an &GNUTLS_STATE structure.
  * @req: is one of GNUTLS_CERT_REQUEST, GNUTLS_CERT_REQUIRE
  *
  * This function specifies if we (in case of a server) are going
  * to send a certificate request message to the client. If 'req'
  * is GNUTLS_CERT_REQUIRE then the server will return an error if
  * the peer does not provide a certificate. If you do not
  * call this function then the client will not be asked to
  * send a certificate.
  **/
void gnutls_certificate_server_set_request(GNUTLS_STATE state,
					    CertificateRequest req)
{
	state->gnutls_internals.send_cert_req = req;
}

/**
  * gnutls_certificate_client_set_select_func - Used to set a callback while selecting the proper (client) certificate
  * @state: is a &GNUTLS_STATE structure.
  * @func: is the callback function
  *
  * The callback's function form is:
  * int (*callback)(GNUTLS_STATE, gnutls_datum *client_cert, int ncerts, gnutls_datum* req_ca_cert, int nreqs);
  *
  * 'client_cert' contains 'ncerts' gnutls_datum structures which hold
  * the raw certificates (DER for X.509 or binary for OpenPGP), of the
  * client.
  *
  * 'req_ca_cert', is only used in X.509 certificates. 
  * Contains a list with the CA names that the server considers trusted. 
  * Normaly we should send a certificate that is signed
  * by one of these CAs. These names are DER encoded. To get a more
  * meaningful value use the function gnutls_x509_extract_dn().
  *
  * This function specifies what we, in case of a client, are going
  * to do when we have to send a certificate. If this callback
  * function is not provided then gnutls will automaticaly try to
  * find an appropriate certificate to send.
  *
  * If the callback function is provided then gnutls will call it
  * once with NULL parameters. If the callback function returns
  * a positive or zero number then gnutls will attempt to automaticaly
  * choose the appropriate certificate. If gnutls fails to find an appropriate
  * certificate, then it will call the callback function again with the 
  * appropriate parameters.
  *
  * In case the callback returned a negative number then gnutls will
  * not attempt to choose the appropriate certificate and will call again
  * the callback function with the appropriate parameters, and rely
  * only to the return value of the callback function.
  *
  * The callback function should return the index of the certificate
  * choosen by the user. -1 indicates that the user
  * does not want to use client authentication.
  *
  * This function returns 0 on success.
  **/
void gnutls_certificate_client_set_select_func(GNUTLS_STATE state,
					     certificate_client_select_func
					     * func)
{
	state->gnutls_internals.client_cert_callback = func;
}

/**
  * gnutls_certificate_server_set_select_func - Used to set a callback while selecting the proper (server) certificate
  * @state: is a &GNUTLS_STATE structure.
  * @func: is the callback function
  *
  * The callback's function form is:
  * int (*callback)(GNUTLS_STATE, gnutls_datum *server_cert, int ncerts);
  *
  * 'server_cert' contains 'ncerts' gnutls_datum structures which hold
  * the raw certificate (DER encoded in X.509) of the server. 
  *
  * This function specifies what we, in case of a server, are going
  * to do when we have to send a certificate. If this callback
  * function is not provided then gnutls will automaticaly try to
  * find an appropriate certificate to send. (actually send the first in the list)
  *
  * In case the callback returned a negative number then gnutls will
  * not attempt to choose the appropriate certificate and the caller function
  * will fail.
  *
  * The callback function will only be called once per handshake.
  * The callback function should return the index of the certificate
  * choosen by the server. -1 indicates an error.
  *
  **/
void gnutls_certificate_server_set_select_func(GNUTLS_STATE state,
					     certificate_server_select_func
					     * func)
{
	state->gnutls_internals.server_cert_callback = func;
}

/*-
  * _gnutls_openpgp_cert_verify_peers - This function returns the peer's certificate status
  * @state: is a gnutls state
  *
  * This function will try to verify the peer's certificate and return it's status (TRUSTED, INVALID etc.). 
  * Returns a negative error code in case of an error, or GNUTLS_E_NO_CERTIFICATE_FOUND if no certificate was sent.
  *
  -*/
int _gnutls_openpgp_cert_verify_peers(GNUTLS_STATE state)
{
	CERTIFICATE_AUTH_INFO info;
	const GNUTLS_CERTIFICATE_CREDENTIALS cred;
	CertificateStatus verify;
	int peer_certificate_list_size;

	CHECK_AUTH(GNUTLS_CRD_CERTIFICATE, GNUTLS_E_INVALID_REQUEST);

	info = _gnutls_get_auth_info(state);
	if (info == NULL)
		return GNUTLS_E_INVALID_REQUEST;

	cred = _gnutls_get_cred(state->gnutls_key, GNUTLS_CRD_CERTIFICATE, NULL);
	if (cred == NULL) {
		gnutls_assert();
		return GNUTLS_E_INSUFICIENT_CRED;
	}

	if (info->raw_certificate_list == NULL || info->ncerts == 0) {
		gnutls_assert();
		return GNUTLS_E_NO_CERTIFICATE_FOUND;
	}
	
	/* generate a list of gnutls_certs based on the auth info
	 * raw certs.
	 */
	peer_certificate_list_size = info->ncerts;

	if (peer_certificate_list_size != 1) {
		gnutls_assert();
		return GNUTLS_E_UNKNOWN_ERROR;
	}
	
	/* Verify certificate 
	 */
	verify = gnutls_openpgp_verify_key( cred->pgp_trustdb, &cred->keyring, &info->raw_certificate_list[0],
				      peer_certificate_list_size);

	if (verify < 0) {
		gnutls_assert();
		return GNUTLS_CERT_INVALID;
	}


	return verify;
}

/**
  * gnutls_certificate_verify_peers - This function returns the peer's certificate verification status
  * @state: is a gnutls state
  *
  * This function will try to verify the peer's certificate and return it's status (TRUSTED, INVALID etc.). 
  * The return value (status) should be one of the CertificateStatus enumerated elements.
  * However you must also check the peer's name in order to check if the verified certificate belongs to the 
  * actual peer. 
  *
  * The return value (status) should be one or more of the CertificateStatus 
  * enumerated elements bitwise or'd. The return value is the same as
  * gnutls_x509_verify_certificate().
  *
  **/
int gnutls_certificate_verify_peers(GNUTLS_STATE state)
{
	CERTIFICATE_AUTH_INFO info;

	CHECK_AUTH(GNUTLS_CRD_CERTIFICATE, GNUTLS_E_INVALID_REQUEST);

	info = _gnutls_get_auth_info(state);
	if (info == NULL) {
		gnutls_assert();
		return GNUTLS_E_NO_CERTIFICATE_FOUND;
	}
	
	if (info->raw_certificate_list == NULL || info->ncerts == 0)
		return GNUTLS_E_NO_CERTIFICATE_FOUND;

	switch( gnutls_cert_type_get( state)) {
		case GNUTLS_CRT_X509:
			return _gnutls_x509_cert_verify_peers( state);
		case GNUTLS_CRT_OPENPGP:
			return _gnutls_openpgp_cert_verify_peers( state);
		default:
			return GNUTLS_E_INVALID_REQUEST;
	}
}

/**
  * gnutls_certificate_expiration_time_peers - This function returns the peer's certificate expiration time
  * @state: is a gnutls state
  *
  * This function will return the peer's certificate expiration time.
  *
  * Returns (time_t) -1 on error.
  *
  **/
time_t gnutls_certificate_expiration_time_peers(GNUTLS_STATE state)
{
	CERTIFICATE_AUTH_INFO info;

	CHECK_AUTH(GNUTLS_CRD_CERTIFICATE, GNUTLS_E_INVALID_REQUEST);

	info = _gnutls_get_auth_info(state);
	if (info == NULL) {
		gnutls_assert();
		return (time_t) -1;
	}
	
	if (info->raw_certificate_list == NULL || info->ncerts == 0) {
		gnutls_assert();
		return (time_t) -1;
	}

	switch( gnutls_cert_type_get( state)) {
		case GNUTLS_CRT_X509:
			return gnutls_x509_extract_certificate_expiration_time(
				&info->raw_certificate_list[0]);
		case GNUTLS_CRT_OPENPGP:
			return gnutls_openpgp_extract_key_expiration_time(
				&info->raw_certificate_list[0]);
		default:
			return (time_t)-1;
	}
}

/**
  * gnutls_certificate_activation_time_peers - This function returns the peer's certificate activation time
  * @state: is a gnutls state
  *
  * This function will return the peer's certificate activation time.
  * This is the creation time for openpgp keys.
  *
  * Returns (time_t) -1 on error.
  *
  **/
time_t gnutls_certificate_activation_time_peers(GNUTLS_STATE state)
{
	CERTIFICATE_AUTH_INFO info;

	CHECK_AUTH(GNUTLS_CRD_CERTIFICATE, GNUTLS_E_INVALID_REQUEST);

	info = _gnutls_get_auth_info(state);
	if (info == NULL) {
		gnutls_assert();
		return (time_t) -1;
	}
	
	if (info->raw_certificate_list == NULL || info->ncerts == 0) {
		gnutls_assert();
		return (time_t) -1;
	}

	switch( gnutls_cert_type_get( state)) {
		case GNUTLS_CRT_X509:
			return gnutls_x509_extract_certificate_activation_time(
				&info->raw_certificate_list[0]);
		case GNUTLS_CRT_OPENPGP:
			return gnutls_openpgp_extract_key_creation_time(
				&info->raw_certificate_list[0]);
		default:
			return (time_t)-1;
	}
}