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