slp_auth.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 1999 by Sun Microsystems, Inc.
* All rights reserved.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* This file contains all authentication-related functionality for
* SLP. Two interfaces are exported:
*
* slp_sign: Creates auth blocks for a given piece of data
* slp_verify: Verifies an auth block for a given piece of data.
*
* A shared object which provides crypto-suites and key management
* functionality is dynamically linked in during intialization. If
* the shared object cannot be found, the authentication code aborts
* and an SLP_AUTHENTICATION_FAILED error is returned. Which shared
* object is actually loaded is controlled by the property
* sun.net.slp.authBackend; the value of this property should contain
* either the name of a shared object which implements the necessary
* interfaces, or a full or relative path to such an object. This value
* will be passed to dlopen(3X) to resolve the symbols.
*
* The shared object must implement the following AMI interfaces:
*
* ami_init
* ami_sign
* ami_verify
* ami_get_cert
* ami_get_cert_chain
* ami_strerror
* ami_end
* AMI_MD5WithRSAEncryption_AID
* AMI_SHA1WithDSASignature_AID
*
* See security/ami.h for more info on these interfaces.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <syslog.h>
#include <synch.h>
#include <dlfcn.h>
#include <slp-internal.h>
#include "slp_ami.h"
/* Prototypes for dynamically loaded (dl'd) AMI functions */
static ami_algid **ami_rsa_aid, **ami_dsa_aid;
static AMI_STATUS (*dld_ami_init)(ami_handle_t **, const char *,
const char *, const u_int, const u_int,
const char *);
static AMI_STATUS (*dld_ami_sign)(ami_handle_t *,
const uchar_t *,
const size_t,
const int,
const ami_algid *,
const uchar_t *,
const size_t,
const ami_algid *,
uchar_t **,
size_t *);
static AMI_STATUS (*dld_ami_verify)(ami_handle_t *,
const uchar_t *,
const size_t,
const int,
const ami_algid *,
const uchar_t *,
const size_t,
const ami_algid *,
const uchar_t *,
const size_t);
static AMI_STATUS (*dld_ami_get_cert)(const ami_handle_t *,
const char *,
ami_cert **,
int *);
static AMI_STATUS (*dld_ami_get_cert_chain)(const ami_handle_t *,
const ami_cert *,
const char **,
int flags,
ami_cert **,
int *);
static AMI_STATUS (*dld_ami_str2dn)(const ami_handle_t *,
char *, ami_name **);
static AMI_STATUS (*dld_ami_dn2str)(const ami_handle_t *,
ami_name *, char **);
static void (*dld_ami_free_cert_list)(ami_cert **, int);
static void (*dld_ami_free_dn)(ami_name **);
static char *(*dld_ami_strerror)(const ami_handle_t *, const AMI_STATUS);
static AMI_STATUS (*dld_ami_end)(ami_handle_t *);
/* local utilities */
static SLPError get_security_backend();
static SLPError make_tbs(const char *, struct iovec *, int,
unsigned int, unsigned char **, size_t *);
static SLPError make_authblock(struct iovec *, int, const char *,
time_t, caddr_t *, size_t *);
static SLPError do_verify(unsigned char *, size_t, unsigned short,
const unsigned char *, size_t, const char *);
static char *alias2dn(ami_handle_t *);
static SLPError check_spis(ami_handle_t *, ami_cert *, int, const char *);
static int dncmp(ami_handle_t *, const char *, const char *);
/*
* Creates a cryptographic signature over the components of authiov, and
* creates an auth block from the signature. The auth block is placed
* into msgiov at the index specified by msgiov_index. The timestamp
* for the auth block is given in ts. Caller must free the auth block
* when finished.
*
* Returns SLP_OK on success, SLP_AUTHENTICATION_FAILED on failure.
*/
SLPError slp_sign(struct iovec *authiov, int authiov_len, time_t ts,
struct iovec *msgiov, int msg_index) {
char *sign_as = NULL;
char *alias, *aliasp;
SLPError err = SLP_OK;
unsigned char num_auths = 0;
/* This auth block is always at least 1 byte long, for num auths */
msgiov[msg_index].iov_base = calloc(1, 1);
msgiov[msg_index].iov_len = 1;
/* if security is off, just return the empty auth block */
if (!slp_get_security_on() || slp_get_bypass_auth()) {
return (SLP_OK);
}
/*
* Security is disabled in Solaris 8 due to AMI trouble.
* The pragmas and LINTED suppress "statement not reached"
* compiler and lint warnings, and should be removed when
* security is re-enabled.
*/
return (SLP_SECURITY_UNAVAILABLE);
#pragma error_messages(off, E_STATEMENT_NOT_REACHED)
/* else we should sign this advert */
if (!(sign_as = (char *)SLPGetProperty(SLP_CONFIG_SIGN_AS)) ||
/*LINTED statement not reached*/
!*sign_as) {
slp_err(LOG_INFO, 0, "slp_sign", "No signing identity given");
return (SLP_AUTHENTICATION_FAILED);
}
/* Try to initialize security backend */
if (!(err = get_security_backend()) == SLP_OK) {
return (SLP_AUTHENTICATION_FAILED);
}
/* dup SPI list so we can destructively modify it */
if (!(sign_as = strdup(sign_as))) {
slp_err(LOG_CRIT, 0, "slp_sign", "out of memory");
return (SLP_MEMORY_ALLOC_FAILED);
}
/* For each SPI, create an auth block */
for (aliasp = sign_as; aliasp; ) {
alias = aliasp;
aliasp = slp_utf_strchr(aliasp, ',');
if (aliasp) {
*aliasp++ = 0;
}
/* create an auth block for this SPI */
err = make_authblock(authiov, authiov_len, alias, ts,
&(msgiov[msg_index].iov_base),
(size_t *)&(msgiov[msg_index].iov_len));
if (err == SLP_MEMORY_ALLOC_FAILED) {
goto done;
} else if (err != SLP_OK) {
/* else skip and keep going */
continue;
}
num_auths++;
}
done:
if (sign_as) free(sign_as);
if (err != SLP_OK) {
return (err);
}
if (num_auths == 0) {
return (SLP_AUTHENTICATION_FAILED);
} else {
size_t off = 0;
/* Lay in number of auth blocks created */
err = slp_add_byte(msgiov[msg_index].iov_base, 1, num_auths, &off);
}
return (err);
#pragma error_messages(on, E_STATEMENT_NOT_REACHED)
}
/*
* Verifies that the signature(s) contained in authblocks validates
* the data in authiov. slp_verify will not read more than len bytes
* from authblocks. n is the stated number of authblocks in authblock.
* The total length of all auth blocks read is placed in *total.
*
* Returns SLP_OK if the verification succeeds.
*/
SLPError slp_verify(struct iovec *authiov, int authiov_len,
const char *authblocks, size_t len, int n, size_t *total) {
int i;
size_t off, this_ab;
unsigned short bsd, ablen;
unsigned int timestamp;
char *spi = NULL;
SLPError err = SLP_AUTHENTICATION_FAILED;
unsigned char *inbytes = NULL;
size_t inbytes_len;
unsigned char *sig;
size_t siglen;
/* 1st: if bypass_auth == true, just return SLP_OK */
if (slp_get_bypass_auth()) {
return (SLP_OK);
}
/* 2nd: If security is off, and there are no auth blocks, OK */
if (!slp_get_security_on() && n == 0) {
return (SLP_OK);
}
/*
* Security is disabled in Solaris 8 due to AMI trouble.
* The pragmas and LINTED suppress "statement not reached"
* compiler and lint warnings, and should be removed when
* security is re-enabled.
*/
return (SLP_SECURITY_UNAVAILABLE);
#pragma error_messages(off, E_STATEMENT_NOT_REACHED)
/* For all other scenarios, we must verify the auth blocks */
/*LINTED statement not reached*/
if (get_security_backend() != SLP_OK || n == 0) {
return (SLP_AUTHENTICATION_FAILED);
}
/*
* If we get here, the backend is available and there are auth
* blocks to verify. Verify each input auth block.
*/
off = 0; /* offset into raw auth blocks */
for (i = 0; i < n && off <= len; i++) {
this_ab = off;
/* BSD */
if ((err = slp_get_sht(authblocks, len, &off, &bsd)) != SLP_OK) {
slp_err(LOG_INFO, 0, "slp_verify", "corrupt auth block");
goto done;
}
/* Auth block length */
if ((err = slp_get_sht(authblocks, len, &off, &ablen)) != SLP_OK) {
slp_err(LOG_INFO, 0, "slp_verify", "corrupt auth block");
goto done;
}
/* Time stamp */
if ((err = slp_get_int32(authblocks, len, &off, &timestamp))
!= SLP_OK) {
slp_err(LOG_INFO, 0, "slp_verify", "corrupt auth block");
goto done;
}
/* SPI string */
if ((err = slp_get_string(authblocks, len, &off, &spi))
!= SLP_OK) {
slp_err(LOG_INFO, 0, "slp_verify", "corrupt auth block");
goto done;
}
err = make_tbs(
spi, authiov, authiov_len, timestamp, &inbytes, &inbytes_len);
if (err != SLP_OK) {
goto done;
}
sig = (unsigned char *)(authblocks + off);
siglen = ablen - (off - this_ab);
off += siglen;
err = do_verify(inbytes, inbytes_len, bsd, sig, siglen, spi);
if (err != SLP_OK) {
free(spi);
goto done;
}
free(spi);
}
done:
if (inbytes) free(inbytes);
*total = off;
return (err);
#pragma error_messages(on, E_STATEMENT_NOT_REACHED)
}
/*
* When first called, attempts to dlopen a security shared library
* and dlsym in the necessary interfaces. The library remains mapped
* in, so successive calls just return SLP_OK.
*/
static SLPError get_security_backend() {
static mutex_t be_lock = DEFAULTMUTEX;
static void *dl = NULL;
static int got_backend = 0;
SLPError err = SLP_SECURITY_UNAVAILABLE;
const char *libname;
char *dlerr;
(void) mutex_lock(&be_lock);
if (got_backend) {
(void) mutex_unlock(&be_lock);
return (SLP_OK);
}
if (!(libname = SLPGetProperty(SLP_CONFIG_AUTH_BACKEND)) ||
!*libname) {
/* revert to default */
libname = "libami.so.1";
}
if (!(dl = dlopen(libname, RTLD_LAZY))) {
dlerr = dlerror();
slp_err(LOG_INFO, 0, "get_security_backend",
"Could not dlopen AMI library: %s",
(dlerr ? dlerr : "unknown DL error"));
slp_err(LOG_INFO, 0, "get_security_backend",
"Is AMI installed?");
goto done;
}
/* Relocate AMI's statically initialized AIDs we need */
if (!(ami_rsa_aid =
dlsym(dl, "AMI_MD5WithRSAEncryption_AID"))) {
dlerr = dlerror();
slp_err(LOG_INFO, 0, "get_security_backend",
"Could not relocate AMI_MD5WithRSAEncryption_AID: %s",
(dlerr ? dlerr : "unknown DL error"));
goto done;
}
if (!(ami_dsa_aid =
dlsym(dl, "AMI_SHA1WithDSASignature_AID"))) {
dlerr = dlerror();
slp_err(LOG_INFO, 0, "get_security_backend",
"Could not relocate AMI_SHA1WithDSASignature_AID: %s",
(dlerr ? dlerr : "unknown DL error"));
goto done;
}
/* Bring in the functions we need */
if (!(dld_ami_init = (AMI_STATUS (*)(ami_handle_t **,
const char *,
const char *,
const u_int,
const u_int,
const char *))dlsym(
dl, "ami_init"))) {
slp_err(LOG_INFO, 0, "get_security_backend",
"Could not load ami_init");
goto done;
}
if (!(dld_ami_sign = (AMI_STATUS (*)(ami_handle_t *,
const uchar_t *,
const size_t,
const int,
const ami_algid *,
const uchar_t *,
const size_t,
const ami_algid *,
uchar_t **,
size_t *))dlsym(
dl, "ami_sign"))) {
slp_err(LOG_INFO, 0, "get_security_backend",
"Could not load ami_sign");
goto done;
}
if (!(dld_ami_verify = (AMI_STATUS (*)(ami_handle_t *,
const uchar_t *,
const size_t,
const int,
const ami_algid *,
const uchar_t *,
const size_t,
const ami_algid *,
const uchar_t *,
const size_t))dlsym(
dl, "ami_verify"))) {
slp_err(LOG_INFO, 0, "get_security_backend",
"Could not load ami_verify");
goto done;
}
if (!(dld_ami_get_cert = (AMI_STATUS (*)(const ami_handle_t *,
const char *,
ami_cert **,
int *))dlsym(
dl, "ami_get_cert"))) {
slp_err(LOG_INFO, 0, "get_security_backend",
"Could not load ami_get_cert");
goto done;
}
if (!(dld_ami_get_cert_chain = (AMI_STATUS (*)(const ami_handle_t *,
const ami_cert *,
const char **,
int flags,
ami_cert **,
int *))dlsym(
dl, "ami_get_cert_chain"))) {
slp_err(LOG_INFO, 0, "get_security_backend",
"Could not load ami_get_cert_chain");
goto done;
}
if (!(dld_ami_str2dn = (AMI_STATUS (*)(const ami_handle_t *,
char *, ami_name **))dlsym(
dl, "ami_str2dn"))) {
slp_err(LOG_INFO, 0, "get_security_backend",
"Could not load ami_str2dn");
goto done;
}
if (!(dld_ami_dn2str = (AMI_STATUS (*)(const ami_handle_t *,
ami_name *, char **))dlsym(
dl, "ami_dn2str"))) {
slp_err(LOG_INFO, 0, "get_security_backend",
"Could not load ami_dn2str");
goto done;
}
if (!(dld_ami_free_cert_list = (void (*)(ami_cert **, int))dlsym(
dl, "ami_free_cert_list"))) {
slp_err(LOG_INFO, 0, "get_security_backend",
"Could not load ami_free_cert_list");
goto done;
}
if (!(dld_ami_free_dn = (void (*)(ami_name **))dlsym(
dl, "ami_free_dn"))) {
slp_err(LOG_INFO, 0, "get_security_backend",
"Could not load ami_free_dn");
goto done;
}
if (!(dld_ami_strerror = (char *(*)(const ami_handle_t *,
const AMI_STATUS))dlsym(
dl, "ami_strerror"))) {
slp_err(LOG_INFO, 0, "get_security_backend",
"Could not load ami_strerror");
goto done;
}
if (!(dld_ami_end = (AMI_STATUS (*)(ami_handle_t *))dlsym(
dl, "ami_end"))) {
slp_err(LOG_INFO, 0, "get_security_backend",
"Could not load ami_end");
goto done;
}
got_backend = 1;
err = SLP_OK;
done:
if (!got_backend && dl) {
(void) dlclose(dl);
}
(void) mutex_unlock(&be_lock);
return (err);
}
/*
* Creates a bytes to-be-signed buffer suitable for input
* a signature algorithm.
*
* The only backend currently available is AMI, which does
* not support incremental updates for digesting. Hence we
* must copy all elements of the input iovec into one buffer.
*
* This function allocates a single buffer into *buf big enough
* to hold all necessary elements, sets *buflen to this length, and
* makes a bytes-to-be-signed buffer. Into this buffer is placed
* first the SPI string, then all elements of iov, and finally
* the timestamp. Caller must free *buf.
*
* Returns err != SLP_OK only on catastrophic error.
*/
static SLPError make_tbs(const char *spi,
struct iovec *iov,
int iovlen,
unsigned int timestamp,
unsigned char **buf,
size_t *buflen) {
int i;
caddr_t p;
size_t off;
SLPError err;
*buflen = 2 + strlen(spi);
for (i = 0; i < iovlen; i++) {
*buflen += iov[i].iov_len;
}
*buflen += sizeof (timestamp);
if (!(*buf = malloc(*buflen))) {
slp_err(LOG_CRIT, 0, "slp_sign", "out of memory");
return (SLP_MEMORY_ALLOC_FAILED);
}
/* @@@ ok to use caddr_t? */
p = (caddr_t)*buf;
/* Lay in SPI string */
off = 0;
if ((err = slp_add_string(p, *buflen, spi, &off)) != SLP_OK) {
return (err);
}
p += off;
/* Copy in elements of iov */
for (i = 0; i < iovlen; i++) {
(void) memcpy(p, iov[i].iov_base, iov[i].iov_len);
p += iov[i].iov_len;
off += iov[i].iov_len;
}
/* Lay in timestamp */
return (slp_add_int32((char *)*buf, *buflen, timestamp, &off));
}
/*
* Creates an auth block from the given parameters:
*
* sig_in IN Data to be signed
* sig_in_len IN Length of sig_in
* alias IN signing alias for this auth block
* timestamp IN Timestamp for this auth block
* abs IN/OUT Buffer of accumulated auth blocks
* abs_len IN/OUT Length of abs
*
* For each new auth block, abs is resized as necessary, and the
* new auth block is appended. abs_len is updated accordingly.
*
* Returns SLP_OK if the signing and auth block creation succeeded.
*/
static SLPError make_authblock(struct iovec *authiov, int authiov_len,
const char *alias, time_t timestamp,
caddr_t *abs, size_t *abs_len) {
unsigned char *sig_out = NULL;
size_t sig_out_len = 0;
ami_handle_t *amih = NULL;
AMI_STATUS ami_err;
size_t off = 0;
SLPError err = SLP_OK;
caddr_t ab;
size_t ab_len;
unsigned short bsd;
ami_algid *aid;
char *dn = NULL;
unsigned char *sig_in = NULL;
size_t sig_in_len;
/* Create the signature */
if ((ami_err = dld_ami_init(&amih, alias, NULL, 0, 0, NULL))
!= AMI_OK) {
slp_err(LOG_INFO, 0, "make_authblock", "ami_init failed: %s",
dld_ami_strerror(amih, ami_err));
return (SLP_AUTHENTICATION_FAILED);
}
/* determine our DN, to be used as the SPI */
if (!(dn = alias2dn(amih))) {
err = SLP_AUTHENTICATION_FAILED;
goto done;
}
/* make bytes to-be-signed */
err = make_tbs(
dn, authiov, authiov_len, timestamp, &sig_in, &sig_in_len);
if (err != SLP_OK) {
goto done;
}
/* @@@ determine the AID and BSD for this alias */
bsd = 1;
aid = *ami_rsa_aid;
if ((ami_err = dld_ami_sign(amih, sig_in, sig_in_len, AMI_END_DATA,
NULL, NULL, 0, aid, &sig_out, &sig_out_len))
!= AMI_OK) {
slp_err(LOG_INFO, 0, "make_authblock", "ami_sign failed: %s",
dld_ami_strerror(amih, ami_err));
err = SLP_AUTHENTICATION_FAILED;
goto done;
}
/* We can now calculate the length of the auth block */
ab_len =
2 + /* BSD */
2 + /* length */
4 + /* timestamp */
2 + strlen(dn) + /* SPI string */
sig_out_len; /* the signature */
/* Grow buffer for already-created auth blocks, if necessary */
if (*abs_len != 0) {
if (!(*abs = realloc(*abs, *abs_len + ab_len))) {
slp_err(LOG_CRIT, 0, "make_authblock", "out of memory");
err = SLP_MEMORY_ALLOC_FAILED;
goto done;
}
}
ab = *abs + *abs_len;
*abs_len += ab_len;
/* BSD */
err = slp_add_sht(ab, ab_len, bsd, &off);
/* Auth block length */
if (err == SLP_OK) {
err = slp_add_sht(ab, ab_len, ab_len, &off);
}
/* timestamp */
if (err == SLP_OK) {
err = slp_add_int32(ab, ab_len, timestamp, &off);
}
/* SPI string */
if (err == SLP_OK) {
err = slp_add_string(ab, ab_len, dn, &off);
}
/* Signature */
if (err == SLP_OK) {
(void) memcpy(ab + off, sig_out, sig_out_len);
}
done:
if (amih) {
dld_ami_end(amih);
}
if (dn) free(dn);
if (sig_in) free(sig_in);
if (sig_out) free(sig_out);
if (err == SLP_MEMORY_ALLOC_FAILED) {
/* critical error; abort */
free(*abs);
}
return (err);
}
/*
* The actual verification routine which interacts with the security
* backend to get a certificate for the given SPI and use that cert
* to verify the signature contained in the auth block.
*
* inbytes IN bytes to be verified
* inbytes_len IN length of inbytes
* bsd IN BSD for this signature
* sig IN the signature
* siglen IN length of sig
* spi IN SPI for this signature, not escaped
*
* Returns SLP_OK if the signature is verified, or SLP_AUTHENTICATION_FAILED
* if any error occured.
*/
static SLPError do_verify(unsigned char *inbytes, size_t inbytes_len,
unsigned short bsd, const unsigned char *sig,
size_t siglen, const char *esc_spi) {
AMI_STATUS ami_err;
ami_handle_t *amih = NULL;
SLPError err;
ami_cert *certs = NULL;
int icert, ccnt;
ami_algid *aid;
char *spi = NULL;
/* Get the right AID */
switch (bsd) {
case 1:
aid = *ami_rsa_aid;
break;
case 2:
aid = *ami_dsa_aid;
break;
default:
slp_err(LOG_INFO, 0, "do_verify",
"Unsupported BSD %d for given SPI %s", bsd, spi);
return (SLP_AUTHENTICATION_FAILED);
}
if ((ami_err = dld_ami_init(&amih, spi, NULL, 0, 0, NULL)) != AMI_OK) {
slp_err(LOG_INFO, 0, "do_verify", "ami_init failed: %s",
dld_ami_strerror(amih, ami_err));
return (SLP_AUTHENTICATION_FAILED);
}
/* unescape SPI */
if ((err = SLPUnescape(esc_spi, &spi, SLP_FALSE))) {
goto done;
}
/* get certificate */
if ((ami_err = dld_ami_get_cert(amih, spi, &certs, &ccnt)) != AMI_OK) {
slp_err(LOG_INFO, 0, "do_verify",
"Can not get certificate for %s: %s",
spi, dld_ami_strerror(amih, ami_err));
err = SLP_AUTHENTICATION_FAILED;
goto done;
}
/* @@@ select the right cert, if more than one */
icert = 0;
if ((ami_err = dld_ami_verify(amih, inbytes, inbytes_len, AMI_END_DATA,
certs[icert].info.pubKeyInfo->algorithm,
certs[icert].info.pubKeyInfo->pubKey.value,
certs[icert].info.pubKeyInfo->pubKey.length,
aid, sig, siglen)) != AMI_OK) {
slp_err(LOG_INFO, 0, "do_verify", "ami_verify failed: %s",
dld_ami_strerror(amih, ami_err));
err = SLP_AUTHENTICATION_FAILED;
goto done;
}
err = check_spis(amih, certs, icert, spi);
done:
if (certs) {
dld_ami_free_cert_list(&certs, ccnt);
}
if (amih) {
dld_ami_end(amih);
}
if (spi) free(spi);
return (err);
}
/*
* Gets this process' DN, or returns NULL on failure. Caller must free
* the result. The reslting DN will be escaped.
*/
static char *alias2dn(ami_handle_t *amih) {
ami_cert *certs;
int ccnt;
AMI_STATUS status;
char *answer = NULL;
char *esc_answer;
if ((status = dld_ami_get_cert(amih, NULL, &certs, &ccnt)) != AMI_OK) {
slp_err(LOG_INFO, 0, "alias2dn",
"Can not get my DN: %s",
dld_ami_strerror(amih, status));
return (NULL);
}
if (ccnt == 0) {
slp_err(LOG_INFO, 0, "alias2dn",
"No cert found for myself");
return (NULL);
}
if ((status = dld_ami_dn2str(amih, certs[0].info.subject, &answer))
!= AMI_OK) {
slp_err(LOG_INFO, 0, "alias2dn",
"Can not convert DN to string: %s",
dld_ami_strerror(amih, status));
answer = NULL;
goto done;
}
if (SLPEscape(answer, &esc_answer, SLP_FALSE) != SLP_OK) {
free(answer);
answer = NULL;
} else {
free(answer);
answer = esc_answer;
}
done:
dld_ami_free_cert_list(&certs, ccnt);
return (answer);
}
static SLPError check_spis(ami_handle_t *amih,
ami_cert *certs,
int icert,
const char *spi) {
ami_cert *chain = NULL;
int ccnt;
const char *cas[2];
char *prop_spi;
char *ue_spi;
char *p;
SLPError err;
AMI_STATUS ami_err;
/* If configured SPI == authblock SPI, we are done */
prop_spi = (char *)SLPGetProperty(SLP_CONFIG_SPI);
if (!prop_spi || !*prop_spi) {
slp_err(LOG_INFO, 0, "do_verify", "no SPI configured");
err = SLP_AUTHENTICATION_FAILED;
goto done;
}
/* dup it so we can modify it */
if (!(prop_spi = strdup(prop_spi))) {
slp_err(LOG_CRIT, 0, "do_verify", "out of memory");
err = SLP_MEMORY_ALLOC_FAILED;
goto done;
}
/* if more than one SPI given, discard all but first */
if ((p = slp_utf_strchr(prop_spi, ','))) {
*p = 0;
}
/* unescape configured DNs */
if ((err = SLPUnescape(prop_spi, &ue_spi, SLP_FALSE)) != SLP_OK) {
goto done;
}
free(prop_spi);
prop_spi = ue_spi;
if (dncmp(amih, prop_spi, spi) == 0) {
/* they match, so we are done */
err = SLP_OK;
goto done;
}
/*
* Else we need to traverse the cert chain. ami_get_cert_chain
* verifies each link in the chain, so no need to do it again.
*/
cas[0] = prop_spi;
cas[1] = NULL;
ami_err = dld_ami_get_cert_chain(amih, certs + icert, cas, 0,
&chain, &ccnt);
if (ami_err != AMI_OK) {
slp_err(LOG_INFO, 0, "do_verify",
"can not get cert chain: %s",
dld_ami_strerror(amih, ami_err));
err = SLP_AUTHENTICATION_FAILED;
goto done;
}
err = SLP_OK;
done:
if (chain) {
dld_ami_free_cert_list(&chain, ccnt);
}
if (prop_spi) free(prop_spi);
return (err);
}
static int dncmp(ami_handle_t *amih, const char *s1, const char *s2) {
AMI_STATUS status;
ami_name *dn1 = NULL;
ami_name *dn2 = NULL;
char *dnstr1 = NULL;
char *dnstr2 = NULL;
int answer;
/* Normalize: convert to DN structs and back to strings */
if ((status = dld_ami_str2dn(amih, (char *)s1, &dn1)) != AMI_OK) {
slp_err(LOG_INFO, 0, "dncmp",
"can not create DN structure for %s: %s",
s1,
dld_ami_strerror(amih, status));
answer = 1;
goto done;
}
if ((status = dld_ami_str2dn(amih, (char *)s2, &dn2)) != AMI_OK) {
slp_err(LOG_INFO, 0, "dncmp",
"can not create DN structure for %s: %s",
s2,
dld_ami_strerror(amih, status));
answer = 1;
goto done;
}
/* convert back to strings */
if ((status = dld_ami_dn2str(amih, dn1, &dnstr1)) != AMI_OK) {
slp_err(LOG_INFO, 0, "dncmp",
"can not convert DN to string: %s",
dld_ami_strerror(amih, status));
answer = 1;
goto done;
}
if ((status = dld_ami_dn2str(amih, dn2, &dnstr2)) != AMI_OK) {
slp_err(LOG_INFO, 0, "dncmp",
"can not convert DN to string: %s",
dld_ami_strerror(amih, status));
answer = 1;
goto done;
}
answer = strcasecmp(dnstr1, dnstr2);
done:
if (dn1) {
dld_ami_free_dn(&dn1);
}
if (dn2) {
dld_ami_free_dn(&dn2);
}
if (dnstr1) free(dnstr1);
if (dnstr2) free(dnstr2);
return (answer);
}