From Anon Ymous:

1) Fix a memory leak in cipher_context_create().
2) Fix a goof in the construction of the digest-uri.
3) Allow SASLC_PROP_SERVICENAME to be a hostname qualified comma
delimited list of service names to select from and update the manpage
to reflect this.
4) Make libsaslc.3 pass mdoclint(1).
This commit is contained in:
christos 2011-02-15 18:36:08 +00:00
parent 763d818e6c
commit 1fa7e8d953
2 changed files with 171 additions and 103 deletions

View File

@ -1,4 +1,4 @@
.\" $NetBSD: libsaslc.3,v 1.7 2011/02/12 23:21:32 christos Exp $
.\" $NetBSD: libsaslc.3,v 1.8 2011/02/15 18:36:08 christos Exp $
.\"
.\" Copyright (c) 2010 The NetBSD Foundation, Inc.
.\" All rights reserved.
@ -34,7 +34,7 @@
.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
.\" POSSIBILITY OF SUCH DAMAGE.
.\"
.Dd December 31, 2010
.Dd February 14, 2011
.Dt LIBSASLC 3
.Os
.Sh NAME
@ -364,7 +364,7 @@ encode_stream(saslc_sess_t *sess, int fdin, int fdout)
return 0;
}
.Ed
.Sh CONFIGURATION
.Ss CONFIGURATION
.Nm
uses three types of dictionaries: context (or global), session, and
mechanism, and they are searched in that order by
@ -456,7 +456,7 @@ inclusive.
Any base supported by
.Xr strtoll 3
is allowed.
.Sh PROPERTIES
.Ss PROPERTIES
Most of the control of the
.Nm
behavior is done via setting various properties in the context or
@ -508,7 +508,11 @@ RC4 Cipher with 56 bit key
.Pp
The default value is
.Qq des,3des,rc4,rc4_40,rc4_56,aes .
.Pq Note that Qo aes Qc is not part of the official standard.
.Po
Note that
.Qq aes
is not part of the official standard.
.Pc
Used by the DIGEST-MD5 mechanism.
.It SASLC_PROP_DEBUG Po Qo DEBUG Qc Pc
If true, then enable debug messages.
@ -526,7 +530,7 @@ in the
section below.
.Pc
.It SASLC_PROP_HOSTNAME Po Qo HOSTNAME Qc Pc
The hostname.
The fully qualified domain name of the server host.
Used by the DIGEST-MD5 and GSSAPI mechanisms.
.It SASLC_PROP_MAXBUF Po Qo MAXBUF Qc Pc
The size of the decode buffer.
@ -588,7 +592,23 @@ function.
The service being used, e.g., smtp, imap, etc.
Used by the DIGEST-MD5 and GSSAPI mechanisms.
.It SASLC_PROP_SERVICENAME Po Qo SERVICENAME Qc Pc
The service name to use.
A comma delimited list of possible service names with elements of the
form
.Qq Oo Ao hostname Ac : Oc Ns Ao serv-name Ac
and with the same rules as for the SASLC_PROP_REALM list.
This should only be used if the client uses a DNS name for the service
that is different from the FQDN of the server.
For example, the service name
.Em example.com
might resolve
.Pq via SRV or MX records
into a set of other DNS names, one of which,
.Em mail3.example.com ,
is the FQDN of the server.
.Po
See RFC 2831 section 2.1.2
.Qq serv-name .
.Pc
Used by the DIGEST-MD5 mechanism.
.El
.Pp
@ -613,7 +633,7 @@ This may be overridden by
If set, turn on debugging messages.
This turns on debugging as early as possible and is a global setting.
.El
.Sh GSSAPI and Kerberos
.Ss GSSAPI and Kerberos
The following is a minimal
.Pq Heimdal
Kerberos 5 setup for use with an smtp server that has been configured
@ -742,13 +762,13 @@ su -m postfix -c kinit
.Pp
to obtain a ticket for the postfix user with the postfix credential
and you should be good to go!
.Sh STANDARDS
RFC 2195, RFC 2222, RFC 2245, RFC 2595, RFC 2831, RFC 4422, RFC 4505,
RFC 4616, RFC 4752.
.Sh OTHER IMPLEMENTATIONS
.Sh COMPATIBILITY
There exist other SASL client library implementations including Cyrus SASL
(http://asg.web.cmu.edu/sasl/sasl-library.html) and GNU SASL
(http://www.gnu.org/software/gsasl/).
.Sh STANDARDS
RFC 2195, RFC 2222, RFC 2245, RFC 2595, RFC 2831, RFC 4422, RFC 4505,
RFC 4616, RFC 4752.
.Sh CAVEATS
The API was heavily influenced by its use with
.Xr postfix 1 .

View File

@ -1,4 +1,4 @@
/* $NetBSD: mech_digestmd5.c,v 1.7 2011/02/12 23:21:32 christos Exp $ */
/* $NetBSD: mech_digestmd5.c,v 1.8 2011/02/15 18:36:08 christos Exp $ */
/* Copyright (c) 2010 The NetBSD Foundation, Inc.
* All rights reserved.
@ -35,7 +35,7 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__RCSID("$NetBSD: mech_digestmd5.c,v 1.7 2011/02/12 23:21:32 christos Exp $");
__RCSID("$NetBSD: mech_digestmd5.c,v 1.8 2011/02/15 18:36:08 christos Exp $");
#include <sys/param.h>
@ -57,7 +57,6 @@ __RCSID("$NetBSD: mech_digestmd5.c,v 1.7 2011/02/12 23:21:32 christos Exp $");
#include "msg.h"
#include "saslc_private.h"
/* See RFC 2831. */
/*
@ -220,10 +219,12 @@ typedef struct {
/**
* @brief if possible convert a UTF-8 string to a ISO8859-1 string.
* The caller is responsible for freeing memory.
* @param utf8 original UTF-8 string.
* @param iso8859 pointer to pointer to the malloced ISO8859-1 string.
* @return -1 if the string cannot be translated.
*
* NOTE: this allocates memory for its output and the caller is
* responsible for freeing it.
*/
static int
utf8_to_8859_1(char *utf8, char **iso8859)
@ -270,10 +271,12 @@ utf8_to_8859_1(char *utf8, char **iso8859)
}
/**
* @brief unquote a string by removing escapes. Allocates memory for
* new string which the caller is responsible for freeing.
* @brief unquote a string by removing escapes.
* @param str string to unquote.
* @return NULL on failure
*
* NOTE: this allocates memory for its output and the caller is
* responsible for freeing it.
*/
static char *
unq(const char *str)
@ -641,26 +644,120 @@ saslc__mech_digestmd5_response(saslc__mech_digestmd5_sess_t *ms,
return r;
}
/**
* @brief Choose a string from a user provided host qualified list,
* i.e., a comma delimited list with possible hostname qualifiers on
* the elements.
* @param hqlist a comma delimited list with entries of the form
* "[hostname:]string".
* @param hostname the hostname to use in the selection.
* @return the best matching string or NULL if none found.
*
* NOTE: hqlist must not be NULL.
* NOTE: this allocates memory for its output and the caller is
* responsible for freeing it.
*/
static char *
choose_from_hqlist(const char *hqlist, const char *hostname)
{
list_t *l, *list;
size_t len;
char *p;
list = saslc__list_parse(hqlist);
if (list == NULL)
return NULL;
/*
* If the user provided a list and the caller provided a
* hostname, pick the first string from the list that
* corresponds to the hostname.
*/
if (hostname != NULL) {
len = strlen(hostname);
for (l = list; l != NULL; l = l->next) {
p = l->value + len;
if (strncasecmp(l->value, hostname, len) != 0 ||
*p != ':')
continue;
if (*(++p) != '\0' && isalnum((unsigned char)*p)) {
p = strdup(p);
saslc__list_free(list);
return p;
}
}
}
/*
* If one couldn't be found, look for first string in the list
* without a hostname specifier.
*/
p = NULL;
for (l = list; l != NULL; l = l->next) {
if (strchr(l->value, ':') == NULL) {
p = strdup(l->value);
break;
}
}
saslc__list_free(list);
return p;
}
/**
* @brief builds digesturi string
* @param service service
* @param service_name service name
* @param realm realm
* @param serv_type type of service to use, e.g., "smtp"
* @param host fully-qualified canonical DNS name of host
* @param serv_name service name if it is replicated via DNS records; may
* be NULL.
* @return digesturi string, NULL on failure.
*/
static char *
saslc__mech_digestmd5_digesturi(const char *service, const char *service_name,
const char *realm)
saslc__mech_digestmd5_digesturi(saslc_sess_t *sess, const char *serv_host)
{
saslc__mech_digestmd5_sess_t *ms;
const char *serv_list;
char *serv_name;
const char *serv_type;
char *r;
int rv;
rv = service_name == NULL
? asprintf(&r, "%s/%s", service, realm)
: asprintf(&r, "%s/%s/%s", service, realm, service_name);
if (rv == -1)
return NULL;
ms = sess->mech_sess;
serv_type = saslc_sess_getprop(sess, SASLC_DIGESTMD5_SERVICE);
if (serv_type == NULL) {
saslc__error_set(ERR(sess), ERROR_MECH,
"service is required for an authentication");
return NULL;
}
serv_list = saslc_sess_getprop(sess, SASLC_DIGESTMD5_SERVICENAME);
serv_name = serv_list != NULL
? choose_from_hqlist(serv_list, serv_host) : NULL;
saslc__msg_dbg("%s: serv_name='%s'", __func__, serv_name);
/****************************************************************/
/* digest-uri = "digest-uri" "=" <"> digest-uri-value <"> */
/* digest-uri-value = serv-type "/" host [ "/" serv-name ] */
/* */
/* If the service is not replicated, or the serv-name is */
/* identical to the host, then the serv-name component MUST be */
/* omitted. The service is considered to be replicated if the */
/* client's service-location process involves resolution using */
/* standard DNS lookup operations, and if these operations */
/* involve DNS records (such as SRV, or MX) which resolve one */
/* DNS name into a set of other DNS names. */
/****************************************************************/
rv = serv_name == NULL || strcmp(serv_host, serv_name) == 0
? asprintf(&r, "%s/%s", serv_type, serv_host)
: asprintf(&r, "%s/%s/%s", serv_type, serv_host, serv_name);
if (serv_name != NULL)
free(serv_name);
if (rv == -1) {
saslc__error_set_errno(ERR(sess), ERROR_NOMEM);
return NULL;
}
saslc__msg_dbg("%s: digest-uri='%s'", __func__, r);
return r;
}
@ -742,60 +839,6 @@ stringprep_realms(bool is_utf8, list_t *realms)
return 0;
}
/**
* @brief Choose a realm from a user provided list which may have
* hostname qualifiers.
* @param user_realm a comma delimited list with entries of the form
* "[hostname:]realm".
* @param hostname the hostname of the server provided by the caller.
* @return the best matching realm or NULL
*/
static char *
choose_user_realm(const char *user_realms, const char *hostname)
{
list_t *l, *list;
size_t len;
char *p;
list = saslc__list_parse(user_realms);
if (list == NULL)
return NULL;
/*
* If the user provided a realm list and the caller provided a
* hostname, pick the first realm from the list that
* corresponds to the hostname.
*/
if (hostname != NULL) {
len = strlen(hostname);
for (l = list; l != NULL; l = l->next) {
p = l->value + len;
if (strncasecmp(l->value, hostname, len) != 0 ||
*p != ':')
continue;
if (*(++p) != '\0' && isalnum((unsigned char)*p)) {
p = strdup(p);
saslc__list_free(list);
return p;
}
}
}
/*
* If one couldn't be found, look for first user provided
* realm without a hostname specifier.
*/
p = NULL;
for (l = list; l != NULL; l = l->next) {
if (strchr(l->value, ':') == NULL) {
p = strdup(l->value);
break;
}
}
saslc__list_free(list);
return p;
}
/**
* @brief choose a realm from a list of possible realms provided by the server
* @param sess the session context
@ -804,9 +847,9 @@ choose_user_realm(const char *user_realms, const char *hostname)
* responsibility to free the memory allocated for the return string.
*/
static char *
choose_realm(saslc_sess_t *sess, list_t *realms)
choose_realm(saslc_sess_t *sess, const char *hostname, list_t *realms)
{
const char *hostname, *user_realms;
const char *user_realms;
list_t *l;
char *p;
@ -819,7 +862,6 @@ choose_realm(saslc_sess_t *sess, list_t *realms)
/* computing A1 (see below for details). */
/*****************************************************************/
hostname = saslc_sess_getprop(sess, SASLC_DIGESTMD5_HOSTNAME);
user_realms = saslc_sess_getprop(sess, SASLC_DIGESTMD5_REALM);
/*
@ -833,7 +875,7 @@ choose_realm(saslc_sess_t *sess, list_t *realms)
* default.
*/
if (user_realms != NULL) {
p = choose_user_realm(user_realms, hostname);
p = choose_from_hqlist(user_realms, hostname);
if (p != NULL)
return p;
}
@ -851,7 +893,7 @@ choose_realm(saslc_sess_t *sess, list_t *realms)
* from the challenge.
*/
if (user_realms == NULL ||
(p = choose_user_realm(user_realms, hostname)) == NULL)
(p = choose_from_hqlist(user_realms, hostname)) == NULL)
return strdup(realms->value);
/*
@ -985,15 +1027,18 @@ cipher_context_create(saslc_sess_t *sess, cipher_t cipher, int do_enc, uint8_t *
/* follows: IVs = MD5({Kcs, "aes-128"}) */
/*************************************************************************/
assert(cipher < __arraycount(cipher_ctx_tbl));
if (cipher >= __arraycount(cipher_ctx_tbl)) {
saslc__error_set_errno(ERR(sess), ERROR_BADARG);
return NULL;
}
ctx = malloc(sizeof(*ctx));
if (ctx == NULL) {
saslc__error_set_errno(ERR(sess), ERROR_NOMEM);
return NULL;
}
assert(cipher < __arraycount(cipher_ctx_tbl));
if (cipher >= __arraycount(cipher_ctx_tbl))
return NULL;
ctp = &cipher_ctx_tbl[cipher];
assert(ctp->eval == cipher);
@ -1133,7 +1178,7 @@ cipher_update(cipher_context_t *ctx, void *in, size_t inlen)
* @param outlen decoded output packet length
* @returns 0 on success, -1 on failure
*
* NOTE: this mallocs memory for its output and the caller is
* NOTE: this allocates memory for its output and the caller is
* responsible for freeing it.
*
* integrity (auth-int):
@ -1203,7 +1248,7 @@ encode_buffer(coder_context_t *ctx, const void *in, size_t inlen,
* @returns 0 on success, -1 on failure
*
* NOTE: this modifies the intput buffer!
* NOTE: this mallocs memory for its output and the caller is
* NOTE: this allocates memory for its output and the caller is
* responsible for freeing it.
*
* integrity (auth-int):
@ -1867,10 +1912,9 @@ saslc__mech_digestmd5_response_data(saslc_sess_t *sess)
rdata_t *rdata;
const char *authcid;
const char *authzid;
const char *passwd;
const char *service;
const char *service_name;
const char *hostname;
const char *maxbuf;
const char *passwd;
int rv;
ms = sess->mech_sess;
@ -1892,19 +1936,23 @@ saslc__mech_digestmd5_response_data(saslc_sess_t *sess)
rdata->cipher = rv;
}
service = saslc_sess_getprop(sess, SASLC_DIGESTMD5_SERVICE);
if (service == NULL) {
hostname = saslc_sess_getprop(sess, SASLC_DIGESTMD5_HOSTNAME);
if (hostname == NULL) {
saslc__error_set(ERR(sess), ERROR_MECH,
"service is required for an authentication");
"hostname is required for authentication");
return -1;
}
service_name = saslc_sess_getprop(sess, SASLC_DIGESTMD5_SERVICENAME);
rdata->realm = choose_realm(sess, hostname, cdata->realm);
if (rdata->realm == NULL) {
saslc__error_set(ERR(sess), ERROR_MECH,
"cannot determine the realm");
return -1;
}
rdata->realm = choose_realm(sess, cdata->realm);
rdata->digesturi = saslc__mech_digestmd5_digesturi(service,
service_name, rdata->realm);
rdata->digesturi = saslc__mech_digestmd5_digesturi(sess, hostname);
if (rdata->digesturi == NULL)
return -1; /* error message already set */
authcid = saslc_sess_getprop(sess, SASLC_DIGESTMD5_AUTHCID);
if (authcid == NULL) {