/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (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
* 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
*/
/*
*/
/*
* 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
*
*/
#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 */
const char *);
const uchar_t *,
const size_t,
const int,
const ami_algid *,
const uchar_t *,
const size_t,
const ami_algid *,
uchar_t **,
size_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);
const char *,
ami_cert **,
int *);
const ami_cert *,
const char **,
int flags,
ami_cert **,
int *);
char *, ami_name **);
ami_name *, char **);
/* local utilities */
static SLPError get_security_backend();
unsigned int, unsigned char **, size_t *);
const unsigned char *, size_t, const char *);
static char *alias2dn(ami_handle_t *);
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.
*/
unsigned char num_auths = 0;
/* This auth block is always at least 1 byte long, for num auths */
/* 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);
/* else we should sign this advert */
/*LINTED statement not reached*/
!*sign_as) {
return (SLP_AUTHENTICATION_FAILED);
}
/* Try to initialize security backend */
return (SLP_AUTHENTICATION_FAILED);
}
/* dup SPI list so we can destructively modify it */
return (SLP_MEMORY_ALLOC_FAILED);
}
/* For each SPI, create an auth block */
if (aliasp) {
*aliasp++ = 0;
}
/* create an auth block for this SPI */
if (err == SLP_MEMORY_ALLOC_FAILED) {
goto done;
/* else skip and keep going */
continue;
}
num_auths++;
}
done:
return (err);
}
if (num_auths == 0) {
return (SLP_AUTHENTICATION_FAILED);
} else {
/* Lay in number of auth blocks created */
}
return (err);
}
/*
* 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.
*/
int i;
unsigned int timestamp;
unsigned char *sig;
/* 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);
/* 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 */
/* BSD */
goto done;
}
/* Auth block length */
goto done;
}
/* Time stamp */
!= SLP_OK) {
goto done;
}
/* SPI string */
!= SLP_OK) {
goto done;
}
goto done;
}
goto done;
}
}
done:
return (err);
}
/*
* 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 int got_backend = 0;
const char *libname;
char *dlerr;
(void) mutex_lock(&be_lock);
if (got_backend) {
(void) mutex_unlock(&be_lock);
return (SLP_OK);
}
!*libname) {
/* revert to default */
}
"Could not dlopen AMI library: %s",
"Is AMI installed?");
goto done;
}
/* Relocate AMI's statically initialized AIDs we need */
if (!(ami_rsa_aid =
"Could not relocate AMI_MD5WithRSAEncryption_AID: %s",
goto done;
}
if (!(ami_dsa_aid =
"Could not relocate AMI_SHA1WithDSASignature_AID: %s",
goto done;
}
/* Bring in the functions we need */
const char *,
const char *,
const uint_t,
const uint_t,
const char *))dlsym(
dl, "ami_init"))) {
"Could not load ami_init");
goto done;
}
const uchar_t *,
const size_t,
const int,
const ami_algid *,
const uchar_t *,
const size_t,
const ami_algid *,
uchar_t **,
dl, "ami_sign"))) {
"Could not load ami_sign");
goto done;
}
const uchar_t *,
const size_t,
const int,
const ami_algid *,
const uchar_t *,
const size_t,
const ami_algid *,
const uchar_t *,
dl, "ami_verify"))) {
"Could not load ami_verify");
goto done;
}
const char *,
ami_cert **,
int *))dlsym(
dl, "ami_get_cert"))) {
"Could not load ami_get_cert");
goto done;
}
const ami_cert *,
const char **,
int flags,
ami_cert **,
int *))dlsym(
dl, "ami_get_cert_chain"))) {
"Could not load ami_get_cert_chain");
goto done;
}
dl, "ami_str2dn"))) {
"Could not load ami_str2dn");
goto done;
}
dl, "ami_dn2str"))) {
"Could not load ami_dn2str");
goto done;
}
dl, "ami_free_cert_list"))) {
"Could not load ami_free_cert_list");
goto done;
}
dl, "ami_free_dn"))) {
"Could not load ami_free_dn");
goto done;
}
if (!(dld_ami_strerror = (char *(*)(const ami_handle_t *,
const AMI_STATUS))dlsym(
dl, "ami_strerror"))) {
"Could not load ami_strerror");
goto done;
}
dl, "ami_end"))) {
"Could not load ami_end");
goto done;
}
got_backend = 1;
done:
if (!got_backend && 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.
*/
int iovlen,
unsigned int timestamp,
unsigned char **buf,
int i;
caddr_t p;
for (i = 0; i < iovlen; i++) {
}
return (SLP_MEMORY_ALLOC_FAILED);
}
/* @@@ ok to use caddr_t? */
/* Lay in SPI string */
off = 0;
return (err);
}
p += off;
/* Copy in elements of iov */
for (i = 0; i < iovlen; i++) {
}
/* Lay in timestamp */
}
/*
* 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
*
* 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.
*/
unsigned short bsd;
/* Create the signature */
!= AMI_OK) {
return (SLP_AUTHENTICATION_FAILED);
}
/* determine our DN, to be used as the SPI */
goto done;
}
/* make bytes to-be-signed */
goto done;
}
/* @@@ determine the AID and BSD for this alias */
bsd = 1;
aid = *ami_rsa_aid;
!= AMI_OK) {
goto done;
}
/* We can now calculate the length of the auth block */
ab_len =
2 + /* BSD */
2 + /* length */
4 + /* timestamp */
sig_out_len; /* the signature */
/* Grow buffer for already-created auth blocks, if necessary */
if (*abs_len != 0) {
goto done;
}
}
/* BSD */
/* Auth block length */
}
/* timestamp */
}
/* SPI string */
}
/* Signature */
}
done:
if (amih) {
}
if (err == SLP_MEMORY_ALLOC_FAILED) {
/* critical error; abort */
}
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.
*/
/* Get the right AID */
switch (bsd) {
case 1:
aid = *ami_rsa_aid;
break;
case 2:
aid = *ami_dsa_aid;
break;
default:
return (SLP_AUTHENTICATION_FAILED);
}
return (SLP_AUTHENTICATION_FAILED);
}
/* unescape SPI */
goto done;
}
/* get certificate */
"Can not get certificate for %s: %s",
goto done;
}
/* @@@ select the right cert, if more than one */
icert = 0;
goto done;
}
done:
if (certs) {
}
if (amih) {
}
return (err);
}
/*
* Gets this process' DN, or returns NULL on failure. Caller must free
* the result. The reslting DN will be escaped.
*/
int ccnt;
char *esc_answer;
"Can not get my DN: %s",
return (NULL);
}
if (ccnt == 0) {
"No cert found for myself");
return (NULL);
}
!= AMI_OK) {
"Can not convert DN to string: %s",
goto done;
}
} else {
answer = esc_answer;
}
done:
return (answer);
}
int icert,
const char *spi) {
int ccnt;
char *prop_spi;
char *ue_spi;
char *p;
/* If configured SPI == authblock SPI, we are done */
goto done;
}
/* dup it so we can modify it */
goto done;
}
/* if more than one SPI given, discard all but first */
*p = 0;
}
/* unescape configured DNs */
goto done;
}
/* they match, so we are done */
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.
*/
"can not get cert chain: %s",
goto done;
}
done:
if (chain) {
}
return (err);
}
int answer;
/* Normalize: convert to DN structs and back to strings */
"can not create DN structure for %s: %s",
s1,
answer = 1;
goto done;
}
"can not create DN structure for %s: %s",
s2,
answer = 1;
goto done;
}
/* convert back to strings */
"can not convert DN to string: %s",
answer = 1;
goto done;
}
"can not convert DN to string: %s",
answer = 1;
goto done;
}
done:
if (dn1) {
}
if (dn2) {
}
return (answer);
}