#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"
static ami_algid **ami_rsa_aid, **ami_dsa_aid;
static AMI_STATUS (*dld_ami_init)(ami_handle_t **, const char *,
const char *, const uint_t, const uint_t,
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 *);
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 *);
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;
msgiov[msg_index].iov_base = calloc(1, 1);
msgiov[msg_index].iov_len = 1;
if (!slp_get_security_on() || slp_get_bypass_auth()) {
return (SLP_OK);
}
return (SLP_SECURITY_UNAVAILABLE);
if (!(sign_as = (char *)SLPGetProperty(SLP_CONFIG_SIGN_AS)) ||
!*sign_as) {
slp_err(LOG_INFO, 0, "slp_sign", "No signing identity given");
return (SLP_AUTHENTICATION_FAILED);
}
if (!(err = get_security_backend()) == SLP_OK) {
return (SLP_AUTHENTICATION_FAILED);
}
if (!(sign_as = strdup(sign_as))) {
slp_err(LOG_CRIT, 0, "slp_sign", "out of memory");
return (SLP_MEMORY_ALLOC_FAILED);
}
for (aliasp = sign_as; aliasp; ) {
alias = aliasp;
aliasp = slp_utf_strchr(aliasp, ',');
if (aliasp) {
*aliasp++ = 0;
}
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) {
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;
err = slp_add_byte(msgiov[msg_index].iov_base, 1, num_auths, &off);
}
return (err);
}
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;
if (slp_get_bypass_auth()) {
return (SLP_OK);
}
if (!slp_get_security_on() && n == 0) {
return (SLP_OK);
}
return (SLP_SECURITY_UNAVAILABLE);
if (get_security_backend() != SLP_OK || n == 0) {
return (SLP_AUTHENTICATION_FAILED);
}
off = 0;
for (i = 0; i < n && off <= len; i++) {
this_ab = off;
if ((err = slp_get_sht(authblocks, len, &off, &bsd)) != SLP_OK) {
slp_err(LOG_INFO, 0, "slp_verify", "corrupt auth block");
goto done;
}
if ((err = slp_get_sht(authblocks, len, &off, &ablen)) != SLP_OK) {
slp_err(LOG_INFO, 0, "slp_verify", "corrupt auth block");
goto done;
}
if ((err = slp_get_int32(authblocks, len, &off, ×tamp))
!= SLP_OK) {
slp_err(LOG_INFO, 0, "slp_verify", "corrupt auth block");
goto done;
}
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);
}
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) {
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;
}
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;
}
if (!(dld_ami_init = (AMI_STATUS (*)(ami_handle_t **,
const char *,
const char *,
const uint_t,
const uint_t,
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);
}
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);
}
p = (caddr_t)*buf;
off = 0;
if ((err = slp_add_string(p, *buflen, spi, &off)) != SLP_OK) {
return (err);
}
p += off;
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;
}
return (slp_add_int32((char *)*buf, *buflen, timestamp, &off));
}
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;
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);
}
if (!(dn = alias2dn(amih))) {
err = SLP_AUTHENTICATION_FAILED;
goto done;
}
err = make_tbs(
dn, authiov, authiov_len, timestamp, &sig_in, &sig_in_len);
if (err != SLP_OK) {
goto done;
}
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;
}
ab_len =
2 +
2 +
4 +
2 + strlen(dn) +
sig_out_len;
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;
err = slp_add_sht(ab, ab_len, bsd, &off);
if (err == SLP_OK) {
err = slp_add_sht(ab, ab_len, ab_len, &off);
}
if (err == SLP_OK) {
err = slp_add_int32(ab, ab_len, timestamp, &off);
}
if (err == SLP_OK) {
err = slp_add_string(ab, ab_len, dn, &off);
}
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) {
free(*abs);
}
return (err);
}
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;
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);
}
if ((err = SLPUnescape(esc_spi, &spi, SLP_FALSE))) {
goto done;
}
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;
}
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);
}
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;
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;
}
if (!(prop_spi = strdup(prop_spi))) {
slp_err(LOG_CRIT, 0, "do_verify", "out of memory");
err = SLP_MEMORY_ALLOC_FAILED;
goto done;
}
if ((p = slp_utf_strchr(prop_spi, ','))) {
*p = 0;
}
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) {
err = SLP_OK;
goto done;
}
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;
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;
}
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);
}