xref: /illumos-gate/usr/src/lib/libslp/clib/slp_auth.c (revision fcdb3229a31dd4ff700c69238814e326aad49098)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright (c) 1999 by Sun Microsystems, Inc.
24  * All rights reserved.
25  */
26 
27 /*
28  * This file contains all authentication-related functionality for
29  * SLP. Two interfaces are exported:
30  *
31  *  slp_sign:		Creates auth blocks for a given piece of data
32  *  slp_verify:		Verifies an auth block for a given piece of data.
33  *
34  * A shared object which provides crypto-suites and key management
35  * functionality is dynamically linked in during intialization. If
36  * the shared object cannot be found, the authentication code aborts
37  * and an SLP_AUTHENTICATION_FAILED error is returned. Which shared
38  * object is actually loaded is controlled by the property
39  * sun.net.slp.authBackend; the value of this property should contain
40  * either the name of a shared object which implements the necessary
41  * interfaces, or a full or relative path to such an object. This value
42  * will be passed to dlopen(3C) to resolve the symbols.
43  *
44  * The shared object must implement the following AMI interfaces:
45  *
46  *  ami_init
47  *  ami_sign
48  *  ami_verify
49  *  ami_get_cert
50  *  ami_get_cert_chain
51  *  ami_strerror
52  *  ami_end
53  *  AMI_MD5WithRSAEncryption_AID
54  *  AMI_SHA1WithDSASignature_AID
55  *
56  * See security/ami.h for more info on these interfaces.
57  */
58 
59 #include <stdio.h>
60 #include <string.h>
61 #include <stdlib.h>
62 #include <syslog.h>
63 #include <synch.h>
64 #include <dlfcn.h>
65 #include <slp-internal.h>
66 #include "slp_ami.h"
67 
68 /* Prototypes for dynamically loaded (dl'd) AMI functions */
69 static ami_algid **ami_rsa_aid, **ami_dsa_aid;
70 static AMI_STATUS (*dld_ami_init)(ami_handle_t **, const char *,
71 				    const char *, const uint_t, const uint_t,
72 				    const char *);
73 
74 static AMI_STATUS (*dld_ami_sign)(ami_handle_t *,
75 				    const uchar_t *,
76 				    const size_t,
77 				    const int,
78 				    const ami_algid *,
79 				    const uchar_t *,
80 				    const size_t,
81 				    const ami_algid *,
82 				    uchar_t **,
83 				    size_t *);
84 static AMI_STATUS (*dld_ami_verify)(ami_handle_t *,
85 				    const uchar_t *,
86 				    const size_t,
87 				    const int,
88 				    const ami_algid *,
89 				    const uchar_t *,
90 				    const size_t,
91 				    const ami_algid *,
92 				    const uchar_t *,
93 				    const size_t);
94 static AMI_STATUS (*dld_ami_get_cert)(const ami_handle_t *,
95 				    const char *,
96 				    ami_cert **,
97 				    int *);
98 static AMI_STATUS (*dld_ami_get_cert_chain)(const ami_handle_t *,
99 					    const ami_cert *,
100 					    const char **,
101 					    int flags,
102 					    ami_cert **,
103 					    int *);
104 static AMI_STATUS (*dld_ami_str2dn)(const ami_handle_t *,
105 				    char *, ami_name **);
106 static AMI_STATUS (*dld_ami_dn2str)(const ami_handle_t *,
107 				    ami_name *, char **);
108 static void (*dld_ami_free_cert_list)(ami_cert **, int);
109 static void (*dld_ami_free_dn)(ami_name **);
110 static char *(*dld_ami_strerror)(const ami_handle_t *, const AMI_STATUS);
111 static AMI_STATUS (*dld_ami_end)(ami_handle_t *);
112 
113 /* local utilities */
114 static SLPError get_security_backend();
115 static SLPError make_tbs(const char *, struct iovec *, int,
116 			    unsigned int, unsigned char **, size_t *);
117 static SLPError make_authblock(struct iovec *, int, const char *,
118 				time_t, caddr_t *, size_t *);
119 static SLPError do_verify(unsigned char *, size_t, unsigned short,
120 				const unsigned char *, size_t, const char *);
121 static char *alias2dn(ami_handle_t *);
122 static SLPError check_spis(ami_handle_t *, ami_cert *, int, const char *);
123 static int dncmp(ami_handle_t *, const char *, const char *);
124 
125 /*
126  * Creates a cryptographic signature over the components of authiov, and
127  * creates an auth block from the signature. The auth block is placed
128  * into msgiov at the index specified by msgiov_index. The timestamp
129  * for the auth block is given in ts. Caller must free the auth block
130  * when finished.
131  *
132  * Returns SLP_OK on success, SLP_AUTHENTICATION_FAILED on failure.
133  */
slp_sign(struct iovec * authiov,int authiov_len,time_t ts,struct iovec * msgiov,int msg_index)134 SLPError slp_sign(struct iovec *authiov, int authiov_len, time_t ts,
135 		    struct iovec *msgiov, int msg_index) {
136 
137 	char *sign_as = NULL;
138 	char *alias, *aliasp;
139 	SLPError err = SLP_OK;
140 	unsigned char num_auths = 0;
141 
142 	/* This auth block is always at least 1 byte long, for num auths */
143 	msgiov[msg_index].iov_base = calloc(1, 1);
144 	msgiov[msg_index].iov_len = 1;
145 
146 	/* if security is off, just return the empty auth block */
147 	if (!slp_get_security_on() || slp_get_bypass_auth()) {
148 	    return (SLP_OK);
149 	}
150 
151 	/*
152 	 * Security is disabled in Solaris 8 due to AMI trouble.
153 	 * The pragmas and LINTED suppress "statement not reached"
154 	 * compiler and lint warnings, and should be removed when
155 	 * security is re-enabled.
156 	 */
157 	return (SLP_SECURITY_UNAVAILABLE);
158 
159 	/* else we should sign this advert */
160 	if (!(sign_as = (char *)SLPGetProperty(SLP_CONFIG_SIGN_AS)) ||
161 /*LINTED statement not reached*/
162 		!*sign_as) {
163 
164 	    slp_err(LOG_INFO, 0, "slp_sign", "No signing identity given");
165 	    return (SLP_AUTHENTICATION_FAILED);
166 	}
167 
168 	/* Try to initialize security backend */
169 	if (!(err = get_security_backend()) == SLP_OK) {
170 	    return (SLP_AUTHENTICATION_FAILED);
171 	}
172 
173 	/* dup SPI list so we can destructively modify it */
174 	if (!(sign_as = strdup(sign_as))) {
175 	    slp_err(LOG_CRIT, 0, "slp_sign", "out of memory");
176 	    return (SLP_MEMORY_ALLOC_FAILED);
177 	}
178 
179 	/* For each SPI, create an auth block */
180 	for (aliasp = sign_as; aliasp; ) {
181 	    alias = aliasp;
182 	    aliasp = slp_utf_strchr(aliasp, ',');
183 	    if (aliasp) {
184 		*aliasp++ = 0;
185 	    }
186 
187 	    /* create an auth block for this SPI */
188 	    err = make_authblock(authiov, authiov_len, alias, ts,
189 				    &(msgiov[msg_index].iov_base),
190 				    (size_t *)&(msgiov[msg_index].iov_len));
191 	    if (err == SLP_MEMORY_ALLOC_FAILED) {
192 		goto done;
193 	    } else if (err != SLP_OK) {
194 		/* else skip and keep going */
195 		continue;
196 	    }
197 
198 	    num_auths++;
199 	}
200 
201 done:
202 	if (sign_as) free(sign_as);
203 
204 	if (err != SLP_OK) {
205 	    return (err);
206 	}
207 
208 	if (num_auths == 0) {
209 	    return (SLP_AUTHENTICATION_FAILED);
210 	} else {
211 	    size_t off = 0;
212 	    /* Lay in number of auth blocks created */
213 	    err = slp_add_byte(msgiov[msg_index].iov_base, 1, num_auths, &off);
214 	}
215 
216 	return (err);
217 }
218 
219 /*
220  * Verifies that the signature(s) contained in authblocks validates
221  * the data in authiov. slp_verify will not read more than len bytes
222  * from authblocks. n is the stated number of authblocks in authblock.
223  * The total length of all auth blocks read is placed in *total.
224  *
225  * Returns SLP_OK if the verification succeeds.
226  */
slp_verify(struct iovec * authiov,int authiov_len,const char * authblocks,size_t len,int n,size_t * total)227 SLPError slp_verify(struct iovec *authiov, int authiov_len,
228 		    const char *authblocks, size_t len, int n, size_t *total) {
229 	int i;
230 	size_t off, this_ab;
231 	unsigned short bsd, ablen;
232 	unsigned int timestamp;
233 	char *spi = NULL;
234 	SLPError err = SLP_AUTHENTICATION_FAILED;
235 	unsigned char *inbytes = NULL;
236 	size_t inbytes_len;
237 	unsigned char *sig;
238 	size_t siglen;
239 
240 	/* 1st: if bypass_auth == true, just return SLP_OK */
241 	if (slp_get_bypass_auth()) {
242 	    return (SLP_OK);
243 	}
244 
245 	/* 2nd: If security is off, and there are no auth blocks, OK */
246 	if (!slp_get_security_on() && n == 0) {
247 	    return (SLP_OK);
248 	}
249 
250 	/*
251 	 * Security is disabled in Solaris 8 due to AMI trouble.
252 	 * The pragmas and LINTED suppress "statement not reached"
253 	 * compiler and lint warnings, and should be removed when
254 	 * security is re-enabled.
255 	 */
256 	return (SLP_SECURITY_UNAVAILABLE);
257 	/* For all other scenarios, we must verify the auth blocks */
258 /*LINTED statement not reached*/
259 	if (get_security_backend() != SLP_OK || n == 0) {
260 	    return (SLP_AUTHENTICATION_FAILED);
261 	}
262 
263 	/*
264 	 * If we get here, the backend is available and there are auth
265 	 * blocks to verify. Verify each input auth block.
266 	 */
267 	off = 0;	/* offset into raw auth blocks */
268 
269 	for (i = 0; i < n && off <= len; i++) {
270 	    this_ab = off;
271 
272 	    /* BSD */
273 	    if ((err = slp_get_sht(authblocks, len, &off, &bsd)) != SLP_OK) {
274 		slp_err(LOG_INFO, 0, "slp_verify", "corrupt auth block");
275 		goto done;
276 	    }
277 
278 	    /* Auth block length */
279 	    if ((err = slp_get_sht(authblocks, len, &off, &ablen)) != SLP_OK) {
280 		slp_err(LOG_INFO, 0, "slp_verify", "corrupt auth block");
281 		goto done;
282 	    }
283 
284 	    /* Time stamp */
285 	    if ((err = slp_get_int32(authblocks, len, &off, &timestamp))
286 		!= SLP_OK) {
287 		slp_err(LOG_INFO, 0, "slp_verify", "corrupt auth block");
288 		goto done;
289 	    }
290 
291 	    /* SPI string */
292 	    if ((err = slp_get_string(authblocks, len, &off, &spi))
293 		!= SLP_OK) {
294 		slp_err(LOG_INFO, 0, "slp_verify", "corrupt auth block");
295 		goto done;
296 	    }
297 
298 	    err = make_tbs(
299 		spi, authiov, authiov_len, timestamp, &inbytes, &inbytes_len);
300 	    if (err != SLP_OK) {
301 		goto done;
302 	    }
303 
304 	    sig = (unsigned char *)(authblocks + off);
305 	    siglen = ablen - (off - this_ab);
306 
307 	    off += siglen;
308 
309 	    err =  do_verify(inbytes, inbytes_len, bsd, sig, siglen, spi);
310 	    if (err != SLP_OK) {
311 		free(spi);
312 		goto done;
313 	    }
314 
315 	    free(spi);
316 	}
317 
318 done:
319 	if (inbytes) free(inbytes);
320 	*total = off;
321 
322 	return (err);
323 }
324 
325 /*
326  * When first called, attempts to dlopen a security shared library
327  * and dlsym in the necessary interfaces. The library remains mapped
328  * in, so successive calls just return SLP_OK.
329  */
get_security_backend()330 static SLPError get_security_backend() {
331 	static mutex_t be_lock = DEFAULTMUTEX;
332 	static void *dl = NULL;
333 	static int got_backend = 0;
334 	SLPError err = SLP_SECURITY_UNAVAILABLE;
335 	const char *libname;
336 	char *dlerr;
337 
338 	(void) mutex_lock(&be_lock);
339 
340 	if (got_backend) {
341 	    (void) mutex_unlock(&be_lock);
342 	    return (SLP_OK);
343 	}
344 
345 	if (!(libname = SLPGetProperty(SLP_CONFIG_AUTH_BACKEND)) ||
346 	    !*libname) {
347 	    /* revert to default */
348 	    libname = "libami.so.1";
349 	}
350 
351 	if (!(dl = dlopen(libname, RTLD_LAZY))) {
352 	    dlerr = dlerror();
353 	    slp_err(LOG_INFO, 0, "get_security_backend",
354 				"Could not dlopen AMI library: %s",
355 				(dlerr ? dlerr : "unknown DL error"));
356 	    slp_err(LOG_INFO, 0, "get_security_backend",
357 				"Is AMI installed?");
358 	    goto done;
359 	}
360 
361 	/* Relocate AMI's statically initialized AIDs we need */
362 	if (!(ami_rsa_aid =
363 		dlsym(dl, "AMI_MD5WithRSAEncryption_AID"))) {
364 
365 	    dlerr = dlerror();
366 	    slp_err(LOG_INFO, 0, "get_security_backend",
367 		    "Could not relocate AMI_MD5WithRSAEncryption_AID: %s",
368 				(dlerr ? dlerr : "unknown DL error"));
369 	    goto done;
370 	}
371 
372 	if (!(ami_dsa_aid =
373 		dlsym(dl, "AMI_SHA1WithDSASignature_AID"))) {
374 
375 	    dlerr = dlerror();
376 	    slp_err(LOG_INFO, 0, "get_security_backend",
377 		    "Could not relocate AMI_SHA1WithDSASignature_AID: %s",
378 				(dlerr ? dlerr : "unknown DL error"));
379 	    goto done;
380 	}
381 
382 	/* Bring in the functions we need */
383 	if (!(dld_ami_init = (AMI_STATUS (*)(ami_handle_t **,
384 					    const char *,
385 					    const char *,
386 					    const uint_t,
387 					    const uint_t,
388 					    const char *))dlsym(
389 						    dl, "ami_init"))) {
390 	    slp_err(LOG_INFO, 0, "get_security_backend",
391 		    "Could not load ami_init");
392 	    goto done;
393 	}
394 
395 	if (!(dld_ami_sign = (AMI_STATUS (*)(ami_handle_t *,
396 						const uchar_t *,
397 						const size_t,
398 						const int,
399 						const ami_algid *,
400 						const uchar_t *,
401 						const size_t,
402 						const ami_algid *,
403 						uchar_t **,
404 						size_t *))dlsym(
405 							dl, "ami_sign"))) {
406 	    slp_err(LOG_INFO, 0, "get_security_backend",
407 		    "Could not load ami_sign");
408 	    goto done;
409 	}
410 
411 	if (!(dld_ami_verify = (AMI_STATUS (*)(ami_handle_t *,
412 						const uchar_t *,
413 						const size_t,
414 						const int,
415 						const ami_algid *,
416 						const uchar_t *,
417 						const size_t,
418 						const ami_algid *,
419 						const uchar_t *,
420 						const size_t))dlsym(
421 							dl, "ami_verify"))) {
422 	    slp_err(LOG_INFO, 0, "get_security_backend",
423 		    "Could not load ami_verify");
424 	    goto done;
425 	}
426 
427 	if (!(dld_ami_get_cert = (AMI_STATUS (*)(const ami_handle_t *,
428 						const char *,
429 						ami_cert **,
430 						int *))dlsym(
431 							dl, "ami_get_cert"))) {
432 	    slp_err(LOG_INFO, 0, "get_security_backend",
433 		    "Could not load ami_get_cert");
434 	    goto done;
435 	}
436 
437 	if (!(dld_ami_get_cert_chain = (AMI_STATUS (*)(const ami_handle_t *,
438 					    const ami_cert *,
439 					    const char **,
440 					    int flags,
441 					    ami_cert **,
442 					    int *))dlsym(
443 						dl, "ami_get_cert_chain"))) {
444 	    slp_err(LOG_INFO, 0, "get_security_backend",
445 		    "Could not load ami_get_cert_chain");
446 	    goto done;
447 	}
448 
449 	if (!(dld_ami_str2dn = (AMI_STATUS (*)(const ami_handle_t *,
450 						char *, ami_name **))dlsym(
451 							dl, "ami_str2dn"))) {
452 	    slp_err(LOG_INFO, 0, "get_security_backend",
453 		    "Could not load ami_str2dn");
454 	    goto done;
455 	}
456 
457 	if (!(dld_ami_dn2str = (AMI_STATUS (*)(const ami_handle_t *,
458 						ami_name *, char **))dlsym(
459 							dl, "ami_dn2str"))) {
460 	    slp_err(LOG_INFO, 0, "get_security_backend",
461 		    "Could not load ami_dn2str");
462 	    goto done;
463 	}
464 
465 	if (!(dld_ami_free_cert_list = (void (*)(ami_cert **, int))dlsym(
466 						dl, "ami_free_cert_list"))) {
467 		    slp_err(LOG_INFO, 0, "get_security_backend",
468 		    "Could not load ami_free_cert_list");
469 	    goto done;
470 	}
471 
472 	if (!(dld_ami_free_dn = (void (*)(ami_name **))dlsym(
473 							dl, "ami_free_dn"))) {
474 	    slp_err(LOG_INFO, 0, "get_security_backend",
475 		    "Could not load ami_free_dn");
476 	    goto done;
477 	}
478 
479 	if (!(dld_ami_strerror = (char *(*)(const ami_handle_t *,
480 					    const AMI_STATUS))dlsym(
481 						dl, "ami_strerror"))) {
482 	    slp_err(LOG_INFO, 0, "get_security_backend",
483 		    "Could not load ami_strerror");
484 	    goto done;
485 	}
486 
487 	if (!(dld_ami_end = (AMI_STATUS (*)(ami_handle_t *))dlsym(
488 							dl, "ami_end"))) {
489 
490 	    slp_err(LOG_INFO, 0, "get_security_backend",
491 		    "Could not load ami_end");
492 	    goto done;
493 	}
494 
495 	got_backend = 1;
496 	err = SLP_OK;
497 
498 done:
499 	if (!got_backend && dl) {
500 	    (void) dlclose(dl);
501 	}
502 	(void) mutex_unlock(&be_lock);
503 
504 	return (err);
505 }
506 
507 /*
508  * Creates a bytes to-be-signed buffer suitable for input
509  * a signature algorithm.
510  *
511  * The only backend currently available is AMI, which does
512  * not support incremental updates for digesting. Hence we
513  * must copy all elements of the input iovec into one buffer.
514  *
515  * This function allocates a single buffer into *buf big enough
516  * to hold all necessary elements, sets *buflen to this length, and
517  * makes a bytes-to-be-signed buffer. Into this buffer is placed
518  * first the SPI string, then all elements of iov, and finally
519  * the timestamp. Caller must free *buf.
520  *
521  * Returns err != SLP_OK only on catastrophic error.
522  */
make_tbs(const char * spi,struct iovec * iov,int iovlen,unsigned int timestamp,unsigned char ** buf,size_t * buflen)523 static SLPError make_tbs(const char *spi,
524 			    struct iovec *iov,
525 			    int iovlen,
526 			    unsigned int timestamp,
527 			    unsigned char **buf,
528 			    size_t *buflen) {
529 	int i;
530 	caddr_t p;
531 	size_t off;
532 	SLPError err;
533 
534 	*buflen = 2 + strlen(spi);
535 
536 	for (i = 0; i < iovlen; i++) {
537 	    *buflen += iov[i].iov_len;
538 	}
539 
540 	*buflen += sizeof (timestamp);
541 
542 	if (!(*buf = malloc(*buflen))) {
543 	    slp_err(LOG_CRIT, 0, "slp_sign", "out of memory");
544 	    return (SLP_MEMORY_ALLOC_FAILED);
545 	}
546 
547 	/* @@@ ok to use caddr_t? */
548 	p = (caddr_t)*buf;
549 
550 	/* Lay in SPI string */
551 	off = 0;
552 	if ((err = slp_add_string(p, *buflen, spi, &off)) != SLP_OK) {
553 		return (err);
554 	}
555 
556 	p += off;
557 
558 	/* Copy in elements of iov */
559 	for (i = 0; i < iovlen; i++) {
560 	    (void) memcpy(p, iov[i].iov_base, iov[i].iov_len);
561 	    p += iov[i].iov_len;
562 	    off += iov[i].iov_len;
563 	}
564 
565 	/* Lay in timestamp */
566 	return (slp_add_int32((char *)*buf, *buflen, timestamp, &off));
567 }
568 
569 /*
570  * Creates an auth block from the given parameters:
571  *
572  *   sig_in	IN	Data to be signed
573  *   sig_in_len	IN	Length of sig_in
574  *   alias	IN	signing alias for this auth block
575  *   timestamp	IN	Timestamp for this auth block
576  *   abs	IN/OUT	Buffer of accumulated auth blocks
577  *   abs_len	IN/OUT	Length of abs
578  *
579  * For each new auth block, abs is resized as necessary, and the
580  * new auth block is appended. abs_len is updated accordingly.
581  *
582  * Returns SLP_OK if the signing and auth block creation succeeded.
583  */
make_authblock(struct iovec * authiov,int authiov_len,const char * alias,time_t timestamp,caddr_t * abs,size_t * abs_len)584 static SLPError make_authblock(struct iovec *authiov, int authiov_len,
585 				const char *alias, time_t timestamp,
586 				caddr_t *abs, size_t *abs_len) {
587 
588 	unsigned char *sig_out = NULL;
589 	size_t sig_out_len = 0;
590 	ami_handle_t *amih = NULL;
591 	AMI_STATUS ami_err;
592 	size_t off = 0;
593 	SLPError err = SLP_OK;
594 	caddr_t ab;
595 	size_t ab_len;
596 	unsigned short bsd;
597 	ami_algid *aid;
598 	char *dn = NULL;
599 	unsigned char *sig_in = NULL;
600 	size_t sig_in_len;
601 
602 	/* Create the signature */
603 	if ((ami_err = dld_ami_init(&amih, alias, NULL, 0, 0, NULL))
604 	    != AMI_OK) {
605 	    slp_err(LOG_INFO, 0, "make_authblock", "ami_init failed: %s",
606 		    dld_ami_strerror(amih, ami_err));
607 	    return (SLP_AUTHENTICATION_FAILED);
608 	}
609 
610 	/* determine our DN, to be used as the SPI */
611 	if (!(dn = alias2dn(amih))) {
612 	    err = SLP_AUTHENTICATION_FAILED;
613 	    goto done;
614 	}
615 
616 	/* make bytes to-be-signed */
617 	err = make_tbs(
618 		dn, authiov, authiov_len, timestamp, &sig_in, &sig_in_len);
619 	if (err != SLP_OK) {
620 	    goto done;
621 	}
622 
623 	/* @@@ determine the AID and BSD for this alias */
624 	bsd = 1;
625 	aid = *ami_rsa_aid;
626 
627 	if ((ami_err = dld_ami_sign(amih, sig_in, sig_in_len, AMI_END_DATA,
628 				NULL, NULL, 0, aid, &sig_out, &sig_out_len))
629 	    != AMI_OK) {
630 
631 		slp_err(LOG_INFO, 0, "make_authblock", "ami_sign failed: %s",
632 			dld_ami_strerror(amih, ami_err));
633 		err = SLP_AUTHENTICATION_FAILED;
634 		goto done;
635 	    }
636 
637 	/* We can now calculate the length of the auth block */
638 	ab_len =
639 		2 +			/* BSD */
640 		2 +			/* length */
641 		4 +			/* timestamp */
642 		2 + strlen(dn) +	/* SPI string */
643 		sig_out_len;		/* the signature */
644 
645 	/* Grow buffer for already-created auth blocks, if necessary */
646 	if (*abs_len != 0) {
647 	    if (!(*abs = realloc(*abs, *abs_len + ab_len))) {
648 		slp_err(LOG_CRIT, 0, "make_authblock", "out of memory");
649 		err = SLP_MEMORY_ALLOC_FAILED;
650 		goto done;
651 	    }
652 	}
653 	ab = *abs + *abs_len;
654 	*abs_len += ab_len;
655 
656 	/* BSD */
657 	err = slp_add_sht(ab, ab_len, bsd, &off);
658 
659 	/* Auth block length */
660 	if (err == SLP_OK) {
661 	    err = slp_add_sht(ab, ab_len, ab_len, &off);
662 	}
663 
664 	/* timestamp */
665 	if (err == SLP_OK) {
666 	    err = slp_add_int32(ab, ab_len, timestamp, &off);
667 	}
668 
669 	/* SPI string */
670 	if (err == SLP_OK) {
671 	    err = slp_add_string(ab, ab_len, dn, &off);
672 	}
673 
674 	/* Signature */
675 	if (err == SLP_OK) {
676 	    (void) memcpy(ab + off, sig_out, sig_out_len);
677 	}
678 
679 done:
680 	if (amih) {
681 	    dld_ami_end(amih);
682 	}
683 	if (dn) free(dn);
684 
685 	if (sig_in) free(sig_in);
686 	if (sig_out) free(sig_out);
687 
688 	if (err == SLP_MEMORY_ALLOC_FAILED) {
689 	    /* critical error; abort */
690 	    free(*abs);
691 	}
692 
693 	return (err);
694 }
695 
696 /*
697  * The actual verification routine which interacts with the security
698  * backend to get a certificate for the given SPI and use that cert
699  * to verify the signature contained in the auth block.
700  *
701  * inbytes	IN	bytes to be verified
702  * inbytes_len	IN	length of inbytes
703  * bsd		IN	BSD for this signature
704  * sig		IN	the signature
705  * siglen	IN	length of sig
706  * spi		IN	SPI for this signature, not escaped
707  *
708  * Returns SLP_OK if the signature is verified, or SLP_AUTHENTICATION_FAILED
709  * if any error occured.
710  */
do_verify(unsigned char * inbytes,size_t inbytes_len,unsigned short bsd,const unsigned char * sig,size_t siglen,const char * esc_spi)711 static SLPError do_verify(unsigned char *inbytes, size_t inbytes_len,
712 			    unsigned short bsd, const unsigned char *sig,
713 			    size_t siglen, const char *esc_spi) {
714 
715 	AMI_STATUS ami_err;
716 	ami_handle_t *amih = NULL;
717 	SLPError err;
718 	ami_cert *certs = NULL;
719 	int icert, ccnt;
720 	ami_algid *aid;
721 	char *spi = NULL;
722 
723 	/* Get the right AID */
724 	switch (bsd) {
725 	case 1:
726 		aid = *ami_rsa_aid;
727 		break;
728 	case 2:
729 		aid = *ami_dsa_aid;
730 		break;
731 	default:
732 		slp_err(LOG_INFO, 0, "do_verify",
733 			"Unsupported BSD %d for given SPI %s", bsd, spi);
734 		return (SLP_AUTHENTICATION_FAILED);
735 	}
736 
737 	if ((ami_err = dld_ami_init(&amih, spi, NULL, 0, 0, NULL)) != AMI_OK) {
738 	    slp_err(LOG_INFO, 0, "do_verify", "ami_init failed: %s",
739 		    dld_ami_strerror(amih, ami_err));
740 	    return (SLP_AUTHENTICATION_FAILED);
741 	}
742 
743 	/* unescape SPI */
744 	if ((err = SLPUnescape(esc_spi, &spi, SLP_FALSE))) {
745 	    goto done;
746 	}
747 
748 	/* get certificate */
749 	if ((ami_err = dld_ami_get_cert(amih, spi, &certs, &ccnt)) != AMI_OK) {
750 	    slp_err(LOG_INFO, 0, "do_verify",
751 		    "Can not get certificate for %s: %s",
752 		    spi, dld_ami_strerror(amih, ami_err));
753 	    err = SLP_AUTHENTICATION_FAILED;
754 	    goto done;
755 	}
756 
757 	/* @@@ select the right cert, if more than one */
758 	icert = 0;
759 
760 	if ((ami_err = dld_ami_verify(amih, inbytes, inbytes_len, AMI_END_DATA,
761 				certs[icert].info.pubKeyInfo->algorithm,
762 				certs[icert].info.pubKeyInfo->pubKey.value,
763 				certs[icert].info.pubKeyInfo->pubKey.length,
764 				aid, sig, siglen)) != AMI_OK) {
765 
766 	    slp_err(LOG_INFO, 0, "do_verify", "ami_verify failed: %s",
767 		    dld_ami_strerror(amih, ami_err));
768 	    err = SLP_AUTHENTICATION_FAILED;
769 	    goto done;
770 	}
771 
772 	err = check_spis(amih, certs, icert, spi);
773 
774 done:
775 	if (certs) {
776 	    dld_ami_free_cert_list(&certs, ccnt);
777 	}
778 
779 	if (amih) {
780 	    dld_ami_end(amih);
781 	}
782 
783 	if (spi) free(spi);
784 
785 	return (err);
786 }
787 
788 /*
789  * Gets this process' DN, or returns NULL on failure. Caller must free
790  * the result. The reslting DN will be escaped.
791  */
alias2dn(ami_handle_t * amih)792 static char *alias2dn(ami_handle_t *amih) {
793 	ami_cert *certs;
794 	int ccnt;
795 	AMI_STATUS status;
796 	char *answer = NULL;
797 	char *esc_answer;
798 
799 	if ((status = dld_ami_get_cert(amih, NULL, &certs, &ccnt)) != AMI_OK) {
800 	    slp_err(LOG_INFO, 0, "alias2dn",
801 		    "Can not get my DN: %s",
802 		    dld_ami_strerror(amih, status));
803 	    return (NULL);
804 	}
805 
806 	if (ccnt == 0) {
807 	    slp_err(LOG_INFO, 0, "alias2dn",
808 		    "No cert found for myself");
809 	    return (NULL);
810 	}
811 
812 	if ((status = dld_ami_dn2str(amih, certs[0].info.subject, &answer))
813 	    != AMI_OK) {
814 	    slp_err(LOG_INFO, 0, "alias2dn",
815 		    "Can not convert DN to string: %s",
816 		    dld_ami_strerror(amih, status));
817 	    answer = NULL;
818 	    goto done;
819 	}
820 
821 	if (SLPEscape(answer, &esc_answer, SLP_FALSE) != SLP_OK) {
822 	    free(answer);
823 	    answer = NULL;
824 	} else {
825 	    free(answer);
826 	    answer = esc_answer;
827 	}
828 
829 done:
830 	dld_ami_free_cert_list(&certs, ccnt);
831 
832 	return (answer);
833 }
834 
check_spis(ami_handle_t * amih,ami_cert * certs,int icert,const char * spi)835 static SLPError check_spis(ami_handle_t *amih,
836 			    ami_cert *certs,
837 			    int icert,
838 			    const char *spi) {
839 	ami_cert *chain = NULL;
840 	int ccnt;
841 	const char *cas[2];
842 	char *prop_spi;
843 	char *ue_spi;
844 	char *p;
845 	SLPError err;
846 	AMI_STATUS ami_err;
847 
848 	/* If configured SPI == authblock SPI, we are done */
849 	prop_spi = (char *)SLPGetProperty(SLP_CONFIG_SPI);
850 	if (!prop_spi || !*prop_spi) {
851 	    slp_err(LOG_INFO, 0, "do_verify", "no SPI configured");
852 	    err = SLP_AUTHENTICATION_FAILED;
853 	    goto done;
854 	}
855 
856 	/* dup it so we can modify it */
857 	if (!(prop_spi = strdup(prop_spi))) {
858 	    slp_err(LOG_CRIT, 0, "do_verify", "out of memory");
859 	    err = SLP_MEMORY_ALLOC_FAILED;
860 	    goto done;
861 	}
862 
863 	/* if more than one SPI given, discard all but first */
864 	if ((p = slp_utf_strchr(prop_spi, ','))) {
865 	    *p = 0;
866 	}
867 
868 	/* unescape configured DNs */
869 	if ((err = SLPUnescape(prop_spi, &ue_spi, SLP_FALSE)) != SLP_OK) {
870 	    goto done;
871 	}
872 	free(prop_spi);
873 	prop_spi = ue_spi;
874 
875 	if (dncmp(amih, prop_spi, spi) == 0) {
876 	    /* they match, so we are done */
877 	    err = SLP_OK;
878 	    goto done;
879 	}
880 
881 	/*
882 	 * Else we need to traverse the cert chain. ami_get_cert_chain
883 	 * verifies each link in the chain, so no need to do it again.
884 	 */
885 	cas[0] = prop_spi;
886 	cas[1] = NULL;
887 	ami_err = dld_ami_get_cert_chain(amih, certs + icert, cas, 0,
888 						&chain, &ccnt);
889 	if (ami_err != AMI_OK) {
890 	    slp_err(LOG_INFO, 0, "do_verify",
891 		    "can not get cert chain: %s",
892 		    dld_ami_strerror(amih, ami_err));
893 	    err = SLP_AUTHENTICATION_FAILED;
894 	    goto done;
895 	}
896 
897 	err = SLP_OK;
898 
899 done:
900 	if (chain) {
901 	    dld_ami_free_cert_list(&chain, ccnt);
902 	}
903 
904 	if (prop_spi) free(prop_spi);
905 
906 	return (err);
907 }
908 
dncmp(ami_handle_t * amih,const char * s1,const char * s2)909 static int dncmp(ami_handle_t *amih, const char *s1, const char *s2) {
910 	AMI_STATUS status;
911 	ami_name *dn1 = NULL;
912 	ami_name *dn2 = NULL;
913 	char *dnstr1 = NULL;
914 	char *dnstr2 = NULL;
915 	int answer;
916 
917 	/* Normalize: convert to DN structs and back to strings */
918 	if ((status = dld_ami_str2dn(amih, (char *)s1, &dn1)) != AMI_OK) {
919 	    slp_err(LOG_INFO, 0, "dncmp",
920 		    "can not create DN structure for %s: %s",
921 		    s1,
922 		    dld_ami_strerror(amih, status));
923 	    answer = 1;
924 	    goto done;
925 	}
926 
927 	if ((status = dld_ami_str2dn(amih, (char *)s2, &dn2)) != AMI_OK) {
928 	    slp_err(LOG_INFO, 0, "dncmp",
929 		    "can not create DN structure for %s: %s",
930 		    s2,
931 		    dld_ami_strerror(amih, status));
932 	    answer = 1;
933 	    goto done;
934 	}
935 
936 	/* convert back to strings */
937 	if ((status = dld_ami_dn2str(amih, dn1, &dnstr1)) != AMI_OK) {
938 	    slp_err(LOG_INFO, 0, "dncmp",
939 		    "can not convert DN to string: %s",
940 		    dld_ami_strerror(amih, status));
941 	    answer = 1;
942 	    goto done;
943 	}
944 
945 	if ((status = dld_ami_dn2str(amih, dn2, &dnstr2)) != AMI_OK) {
946 	    slp_err(LOG_INFO, 0, "dncmp",
947 		    "can not convert DN to string: %s",
948 		    dld_ami_strerror(amih, status));
949 	    answer = 1;
950 	    goto done;
951 	}
952 
953 	answer = strcasecmp(dnstr1, dnstr2);
954 
955 done:
956 	if (dn1) {
957 	    dld_ami_free_dn(&dn1);
958 	}
959 
960 	if (dn2) {
961 	    dld_ami_free_dn(&dn2);
962 	}
963 
964 	if (dnstr1) free(dnstr1);
965 	if (dnstr2) free(dnstr2);
966 
967 	return (answer);
968 }
969