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