/*
*/
/* DIGEST-MD5 SASL plugin
* Rob Siemborski
* Tim Martin
* Alexey Melnikov
* $Id: digestmd5.c,v 1.153 2003/03/30 22:17:06 leg Exp $
*/
/*
* Copyright (c) 1998-2003 Carnegie Mellon University. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* distribution.
*
* 3. The name "Carnegie Mellon University" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For permission or any other legal
* details, please contact
* Office of Technology Transfer
* Carnegie Mellon University
* 5000 Forbes Avenue
* Pittsburgh, PA 15213-3890
* (412) 268-4387, fax: (412) 268-7395
* tech-transfer@andrew.cmu.edu
*
* 4. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by Computing Services
* at Carnegie Mellon University (http://www.cmu.edu/computing/)."
*
* CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
* THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
* FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <config.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#ifndef macintosh
#endif
#include <fcntl.h>
#include <ctype.h>
/* EXPORT DELETE START */
/* DES support */
#ifdef WITH_DES
# ifdef WITH_SSL_DES
# else /* system DES library */
# include <des.h>
# endif
#endif /* WITH_DES */
/* EXPORT DELETE END */
#ifdef WIN32
# include <winsock.h>
#else /* Unix */
#endif /* WIN32 */
#ifdef _SUN_SDK_
#include <unistd.h>
#endif /* _SUN_SDK_ */
#include <sasl.h>
#include <saslplug.h>
#include "plugin_common.h"
#include <security/cryptoki.h>
#endif /* _SUN_SDK_ && USE_UEF */
#ifndef WIN32
#endif /* end WIN32 */
#ifdef macintosh
#include <sasl_md5_plugin_decl.h>
#endif
/* external definitions */
#ifndef _SUN_SDK_
#ifdef sun
/* gotta define gethostname ourselves on suns */
extern int gethostname(char *, int);
#endif
#endif /* !_SUN_SDK_ */
#define bool int
#ifndef TRUE
#define FALSE (0)
#endif
/***************************** Common Section *****************************/
#ifndef _SUN_SDK_
#endif /* !_SUN_SDK_ */
/* Definitions */
/* Layer Flags */
/* defines */
const char *SIGNING_CLIENT_SERVER="Digest session key to client-to-server signing key magic constant";
const char *SIGNING_SERVER_CLIENT="Digest session key to server-to-client signing key magic constant";
struct context;
const char *,
unsigned,
unsigned char[],
char *,
unsigned *);
#ifdef _SUN_SDK_
char [16]);
#else
unsigned char [16]);
#endif /* _SUN_SDK_ */
/* cached auth info used for fast reauth */
typedef struct reauth_entry {
char *authid;
char *realm;
unsigned char *nonce;
unsigned int nonce_count;
unsigned char *cnonce;
union {
struct {
} s; /* server stuff */
struct {
char *serverFQDN;
int protection;
unsigned int server_maxbuf;
} c; /* client stuff */
} u;
typedef struct reauth_cache {
/* static stuff */
void *mutex;
reauth_entry_t *e; /* fixed-size hash table of entries */
/* context that stores info */
typedef struct context {
char *authid;
char *realm;
unsigned char *nonce;
unsigned int nonce_count;
unsigned char *cnonce;
char *response_value;
unsigned int seqnum;
/* copy of utils from the params structures */
/* For general use */
char *out_buf;
unsigned out_buf_len;
char *decode_tmp_buf;
unsigned decode_tmp_buf_len;
char *MAC_buf;
unsigned MAC_buf_len;
char *buffer;
int cursize;
/* Layer info */
/* Server MaxBuf for Client or Client MaxBuf For Server */
/* INCOMING */
unsigned int in_maxbuf;
/* if privacy mode is used use these functions for encode and decode */
} context_t;
struct digest_cipher {
char *name;
int n; /* bits to make privacy key */
};
#ifdef _SUN_SDK_
#else
#endif /* _SUN_SDK_ */
/* Hashes a string to produce an unsigned short */
{
unsigned val = 0;
int i;
i = (int) *str;
val ^= i;
val <<= 1;
str++;
}
return val;
}
{
unsigned short i;
unsigned char j;
for (i = 0; i < HASHLEN; i++) {
if (j <= 9)
else
j = Bin[i] & 0xf;
if (j <= 9)
else
}
}
/*
* calculate request-digest/response-digest as per HTTP Digest spec
*/
void
unsigned char *pszNonce, /* nonce from server */
unsigned int pszNonceCount, /* 8 hex digits */
unsigned char *pszCNonce, /* client nonce */
unsigned char *pszQop, /* qop-value: "", "auth",
* "auth-int" */
unsigned char *pszDigestUri, /* requested URL */
unsigned char *pszMethod,
)
{
/* calculate H(A2) */
}
/* utils->MD5Update(&Md5Ctx, (unsigned char *) "AUTHENTICATE:", 13); */
/* append ":00000000000000000000000000000000" */
}
/* calculate response */
if (*pszQop) {
#ifdef _SUN_SDK_
#else
#endif /* _SUN_SDK_ */
}
}
{
if (*scan > 0xC3)
break; /* abort if outside 8859-1 */
break;
}
}
/* if scan >= end, then this is a 8859-1 string. */
}
/*
* if the string is entirely in the 8859-1 subset of UTF-8, then translate to
* 8859-1 prior to MD5
*/
bool In_ISO_8859_1,
const unsigned char *base,
int len)
{
unsigned char cbuf;
/* if we found a character outside 8859-1, don't alter string */
if (!In_ISO_8859_1) {
return;
}
/* convert to 8859-1 prior to applying hash */
do {
break;
}
}
unsigned char *pszUserName,
unsigned char *pszRealm,
unsigned char *Password,
int PasswordLen,
{
bool In_8859_1;
/* Chris Newman clarified that the following text in DIGEST-MD5 spec
is bogus: "if name and password are both in ISO 8859-1 charset"
We should use code example instead */
/* We have to convert UTF-8 to ISO-8859-1 if possible */
/* a NULL realm is equivalent to the empty string */
}
/* We have to convert UTF-8 to ISO-8859-1 if possible */
}
{
unsigned char *base64buf;
int base64len;
return NULL;
#if defined _DEV_URANDOM && defined _SUN_SDK_
{
int nread = 0;
if (fd != -1) {
}
if (nread != NONCE_SIZE)
}
#else
#endif /* _DEV_URANDOM && _SUN_SDK_ */
/* base 64 encode it so it has valid chars */
#ifdef _SUN_SDK_
"Unable to allocate final buffer");
#else
#endif /* _SUN_SDK_ */
return NULL;
}
/*
* Returns SASL_OK on success, SASL_BUFOVER if result won't fit
*/
return NULL;
}
return base64buf;
}
char *name,
unsigned char *value,
bool need_quotes)
{
int ret;
if (need_quotes) {
} else {
}
return SASL_OK;
}
static char *skip_lws (char *s)
{
if(!s) return NULL;
/* skipping spaces: */
if (s[0]=='\0') break;
s++;
}
return s;
}
#ifdef __SUN_SDK_
#else
static char *skip_token (char *s, int caseinsensitive)
#endif /* _SUN_SDK_ */
{
if(!s) return NULL;
#ifdef __SUN_SDK_
while (((unsigned char *)s)[0]>SP) {
#else
while (s[0]>SP) {
#endif /* _SUN_SDK_ */
s[0]=='@' || s[0]==',' || s[0]==';' || s[0]==':' || s[0]=='\\' ||
s[0]=='\'' || s[0]=='/' || s[0]=='[' || s[0]==']' || s[0]== '?' ||
s[0]=='=' || s[0]== '{' || s[0]== '}') {
#ifdef __SUN_SDK_
/* the above chars are never uppercase */
break;
#else
if (caseinsensitive == 1) {
if (!isupper((unsigned char) s[0]))
break;
} else {
break;
}
#endif /* _SUN_SDK_ */
}
s++;
}
return s;
}
/* NULL - error (unbalanced quotes),
otherwise pointer to the first character after value */
{
char *endvalue;
int escaped = 0;
char *outptr;
if (qstr[0] == '"') {
qstr++;
if (escaped) {
escaped = 0;
}
else if (endvalue[0] == '\\') {
escaped = 1;
outptr--; /* Will be incremented at the end of the loop */
}
else if (endvalue[0] == '"') {
break;
}
else {
}
}
if (endvalue[0] != '"') {
return NULL;
}
outptr[0] = '\0';
outptr++;
}
endvalue++;
}
else { /* not qouted value (token) */
};
return endvalue;
}
{
char *endpair;
/* int inQuotes; */
if (curp[0] == '\0') return;
/* skipping spaces: */
/* strip wierd chars */
*curp++ = '\0';
};
return;
}
curp[0] = '\0';
curp++;
return;
}
if (endpair[0] != ',') {
if (endpair[0]!='\0') {
*endpair++ = '\0';
}
}
/* syntax check: MUST be '\0' or ',' */
if (endpair[0] == ',') {
endpair[0] = '\0';
endpair++; /* skipping <,> */
} else if (endpair[0] != '\0') {
return;
}
}
/* EXPORT DELETE START */
#ifdef WITH_DES
struct des_context_s {
};
/* slide the first 7 bytes of 'inbuf' into the high seven bits of the
first 8 bytes of 'keybuf'. 'keybuf' better be 8 bytes long or longer. */
{
}
/******************************
*
* 3DES functions
*
*****************************/
const char *input,
unsigned inputlen,
unsigned char digest[16],
char *output,
unsigned *outputlen)
{
int padding, p;
des_ede2_cbc_encrypt((void *) input,
(void *) output,
c->keysched,
c->keysched2,
&c->ivec,
/* now chop off the padding */
/* invalid padding length */
return SASL_FAIL;
}
/* verify all padding is correct */
for (p = 1; p <= padding; p++) {
return SASL_FAIL;
}
}
/* chop off the padding */
/* copy in the HMAC to digest */
return SASL_OK;
}
const char *input,
unsigned inputlen,
unsigned char digest[16],
char *output,
unsigned *outputlen)
{
int len;
int paddinglen;
/* determine padding length */
/* now construct the full stuff to be ciphered */
des_ede2_cbc_encrypt((void *) output,
(void *) output,
len,
c->keysched,
c->keysched2,
&c->ivec,
return SASL_OK;
}
unsigned char enckey[16],
unsigned char deckey[16])
{
des_context_t *c;
/* allocate enc & dec context */
if (c == NULL) return SASL_NOMEM;
/* setup enc context */
return SASL_FAIL;
return SASL_FAIL;
/* setup dec context */
c++;
return SASL_FAIL;
return SASL_FAIL;
return SASL_OK;
}
/******************************
*
* DES functions
*
*****************************/
const char *input,
unsigned inputlen,
unsigned char digest[16],
char *output,
unsigned *outputlen)
{
int p, padding = 0;
des_cbc_encrypt((void *) input,
(void *) output,
c->keysched,
&c->ivec,
/* Update the ivec (des_cbc_encrypt implementations tend to be broken in
this way) */
/* now chop off the padding */
/* invalid padding length */
return SASL_FAIL;
}
/* verify all padding is correct */
for (p = 1; p <= padding; p++) {
return SASL_FAIL;
}
}
/* chop off the padding */
/* copy in the HMAC to digest */
return SASL_OK;
}
const char *input,
unsigned inputlen,
unsigned char digest[16],
char *output,
unsigned *outputlen)
{
int len;
int paddinglen;
/* determine padding length */
/* now construct the full stuff to be ciphered */
des_cbc_encrypt((void *) output,
(void *) output,
len,
c->keysched,
&c->ivec,
/* Update the ivec (des_cbc_encrypt implementations tend to be broken in
this way) */
return SASL_OK;
}
unsigned char enckey[16],
unsigned char deckey[16])
{
des_context_t *c;
/* allocate enc context */
if (c == NULL) return SASL_NOMEM;
/* setup enc context */
/* setup dec context */
c++;
return SASL_OK;
}
{
/* free des contextss. only cipher_enc_context needs to be free'd,
since cipher_dec_context was allocated at the same time. */
}
#endif /* WITH_DES */
#ifdef WITH_RC4
/* quick generic implementation of RC4 */
struct rc4_context_s {
int i, j;
};
const unsigned char *key,
unsigned keylen)
{
int i, j;
/* fill in linearly s0=0 s1=1... */
for (i=0;i<256;i++)
j=0;
for (i = 0; i < 256; i++) {
unsigned char tmp;
/* j = (j + Si + Ki) mod 256 */
/* swap Si and Sj */
}
/* counters initialized to 0 */
text->i = 0;
text->j = 0;
}
const char *input,
char *output,
unsigned len)
{
int tmp;
int i = text->i;
int j = text->j;
int t;
int K;
i = (i + 1) % 256;
/* swap Si and Sj */
/* byte K is Xor'ed with plaintext */
}
text->i = i;
text->j = j;
}
const char *input,
char *output,
unsigned len)
{
int tmp;
int i = text->i;
int j = text->j;
int t;
int K;
i = (i + 1) % 256;
/* swap Si and Sj */
/* byte K is Xor'ed with plaintext */
}
text->i = i;
text->j = j;
}
{
/* free rc4 context structures */
#ifdef _SUN_SDK_
#endif /* _SUN_SDK_ */
}
#ifdef _SUN_SDK_
char enckey[16],
char deckey[16])
#else
unsigned char enckey[16],
unsigned char deckey[16])
#endif /* _SUN_SDK_ */
{
/* allocate rc4 context structures */
#ifdef _SUN_SDK_
return SASL_NOMEM;
}
#else
#endif /* _SUN_SDK_ */
/* initialize them */
(const unsigned char *) enckey, 16);
(const unsigned char *) deckey, 16);
return SASL_OK;
}
const char *input,
unsigned inputlen,
unsigned char digest[16],
char *output,
unsigned *outputlen)
{
/* decrypt the text part */
/* decrypt the HMAC part */
/* no padding so we just subtract the HMAC to get the text length */
return SASL_OK;
}
const char *input,
unsigned inputlen,
unsigned char digest[16],
char *output,
unsigned *outputlen)
{
/* pad is zero */
/* encrypt the text part */
inputlen);
/* encrypt the HMAC part */
(const char *) digest,
return SASL_OK;
}
#endif /* WITH_RC4 */
/* EXPORT DELETE END */
{
/* EXPORT DELETE START */
#ifdef WITH_RC4
#endif
#ifdef WITH_DES
#endif
/* EXPORT DELETE END */
};
#ifdef USE_UEF
struct uef_context_s {
};
/*
* slide the first 7 bytes of 'inbuf' into the high seven bits of the
* first 8 bytes of 'keybuf'. 'inbuf' better be 8 bytes long or longer.
*
* This is used to compute the IV for "des" and "3des" as described in
* draft-ietf-sasl-rfc2831bis-00.txt - The IV for "des"
* and "3des" is the last 8 bytes of Kcc or Kcs - the encryption keys.
*/
{
}
/*
* Create encryption and decryption session handle handles for later use.
* Returns SASL_OK on success - any other return indicates failure.
*
* free_uef is called to release associated resources by
* digestmd5_common_mech_dispose
*/
char enckey[16],
char deckey[16])
{
{CKA_ENCRYPT, NULL, sizeof (true)},
} else {
}
} else {
}
/* allocate rc4 context structures */
if (enc_context == NULL)
return SASL_NOMEM;
&enc_context->hSession);
#ifdef DEBUG
"enc C_OpenSession Failed:0x%.8X\n", rv);
#endif
return SASL_FAIL;
}
#ifdef DEBUG
"enc C_CreateObject: rv = 0x%.8X\n", rv);
#endif
return SASL_FAIL;
}
/* Initialize the encryption operation in the session */
#ifdef DEBUG
"C_EncryptInit: rv = 0x%.8X\n", rv);
#endif
return SASL_FAIL;
}
if (dec_context == NULL)
return SASL_NOMEM;
&dec_context->hSession);
#ifdef DEBUG
"dec C_OpenSession Failed:0x%.8X\n", rv);
#endif
return SASL_FAIL;
}
}
} else {
}
#ifdef DEBUG
"dec C_CreateObject: rv = 0x%.8X\n", rv);
#endif
return SASL_FAIL;
}
/* Initialize the decryption operation in the session */
#ifdef DEBUG
"C_DecryptInit: rv = 0x%.8X\n", rv);
#endif
return SASL_FAIL;
}
return SASL_OK;
}
char enckey[16],
char deckey[16])
{
}
char enckey[16],
char deckey[16])
{
}
char enckey[16],
char deckey[16])
{
}
static void
{
if (enc_context != NULL) {
#ifdef DEBUG
"C_EncryptFinal failed:0x%.8X\n", rv);
#endif
}
#ifdef DEBUG
"C_DestroyObject failed:0x%.8X\n", rv);
#endif
}
#ifdef DEBUG
"C_CloseSession failed:0x%.8X\n", rv);
#endif
}
}
if (dec_context != NULL) {
#ifdef DEBUG
"C_DecryptFinal failed:0x%.8X\n", rv);
#endif
}
#ifdef DEBUG
"C_DestroyObject failed:0x%.8X\n", rv);
#endif
}
#ifdef DEBUG
"C_CloseSession failed:0x%.8X\n", rv);
#endif
}
}
}
static int
const char *input,
unsigned inputlen,
unsigned char digest[16],
char *output,
unsigned *outputlen)
{
#ifdef DEBUG
"C_DecryptUpdate failed:0x%.8X\n", rv);
#endif
return SASL_FAIL;
}
&ulDigestLen);
#ifdef DEBUG
"C_DecryptUpdate:0x%.8X, digestLen:%d\n",
rv, ulDigestLen);
#endif
return SASL_FAIL;
}
return SASL_OK;
}
static int
const char *input,
unsigned inputlen,
unsigned char digest[16],
char *output,
unsigned *outputlen)
{
#ifdef DEBUG
"C_EncryptUpdate failed: 0x%.8X "
"inputlen:%d outputlen:%d\n",
#endif
return SASL_FAIL;
}
#ifdef DEBUG
"C_EncryptUpdate failed: 0x%.8X ulDigestLen:%d\n",
rv, ulDigestLen);
#endif
return SASL_FAIL;
}
return SASL_OK;
}
static int
const char *input,
unsigned inputlen,
unsigned char digest[16],
char *output,
unsigned *outputlen)
{
int padding, p;
#ifdef DEBUG
"C_DecryptUpdate failed:0x%.8X\n", rv);
#endif
return SASL_FAIL;
}
#ifdef DEBUG
"C_DecryptUpdate unexpected data len:%d !=%d\n",
#endif
return SASL_BUFOVER;
}
/* now chop off the padding */
/* invalid padding length */
return SASL_BADMAC;
}
/* verify all padding is correct */
for (p = 1; p <= padding; p++) {
return SASL_BADMAC;
}
}
/* chop off the padding */
/* copy in the HMAC to digest */
return SASL_OK;
}
static int
const char *input,
unsigned inputlen,
unsigned char digest[16],
char *output,
unsigned *outputlen)
{
int paddinglen;
/* determine padding length */
/* now construct the full stuff to be ciphered */
#ifdef DEBUG
"C_EncryptUpdate failed: 0x%.8X "
"inputlen:%d outputlen:%d\n",
#endif
return SASL_FAIL;
}
return SASL_OK;
}
{
&free_uef },
&free_uef },
&free_uef },
&free_uef },
&free_uef },
};
#endif /* USE_UEF */
const sasl_utils_t *utils,
{
} else {
}
} else {
}
/* create integrity keys */
/* sending */
} else {
}
/* receiving */
} else {
}
return SASL_OK;
}
/* len, CIPHER(Kc, {msg, pag, HMAC(ki, {SeqNum, msg})[0..9]}), x0001, SeqNum */
static int
unsigned numiov,
const char **output,
unsigned *outputlen)
{
int tmp;
unsigned int tmpnum;
unsigned short int tmpshort;
int ret;
char *out;
return SASL_BADPARAM;
}
if (numiov > 1) {
} else {
/* avoid the data copy */
}
/* make sure the output buffer is big enough for this blob */
&(text->encode_buf_len),
(4 + /* for length */
10 + /* for MAC */
8 + /* maximum pad */
6 + /* for padding */
1)); /* trailing null */
/* skip by the length for now */
/* construct (seqnum, msg) */
/* We can just use the output buffer because it's big enough */
/* HMAC(ki, (seqnum, msg) ) */
/* calculate the encrypted part */
/* copy in version */
out+=2;
/* put in seqnum */
/* put the 1st 4 bytes in */
(*outputlen)+=4;
return SASL_OK;
}
static int
const char **input,
unsigned *inputlen,
char **output,
unsigned *outputlen)
{
unsigned int tocopy;
unsigned diff;
int result;
int tmpnum;
int lup;
{
/* if less than 4 bytes just copy those we have into text->size */
if (*inputlen<4)
else
tocopy=4;
{
return SASL_FAIL; /* too big probably error */
}
else
}
*outputlen=0;
if (*inputlen==0) /* have to wait until next time for data */
return SASL_OK;
return SASL_FAIL;
}
return SASL_FAIL;
{
*inputlen=0;
*outputlen=0;
return SASL_OK;
} else {
}
{
unsigned short ver;
unsigned int seqnum;
return result;
return result;
{
int i;
for(i=10; i; i--) {
}
}
/* check the version number */
{
#ifdef _INTEGRATED_SOLARIS_
gettext("Wrong Version"));
#else
#endif /* _INTEGRATED_SOLARIS_ */
return SASL_FAIL;
}
/* check the CMAC */
/* construct (seqnum, msg) */
/* HMAC(ki, (seqnum, msg) ) */
(*outputlen) + 4,
/* now check it */
{
#ifdef _SUN_SDK_
"CMAC doesn't match at byte %d!", lup);
return SASL_BADMAC;
#else
"CMAC doesn't match at byte %d!", lup);
return SASL_FAIL;
#endif /* _SUN_SDK_ */
}
/* check the sequence number */
{
#ifdef _SUN_SDK_
"Incorrect Sequence Number");
#else
"Incorrect Sequence Number");
#endif /* _SUN_SDK_ */
return SASL_FAIL;
}
}
return SASL_OK;
}
{
int ret;
return ret;
}
static int
unsigned numiov,
const char **output,
unsigned *outputlen)
{
unsigned int tmpnum;
unsigned short int tmpshort;
int ret;
return SASL_BADPARAM;
}
if (numiov > 1) {
&text->enc_in_buf);
} else {
/* avoid the data copy */
}
/* construct output */
/* construct (seqnum, msg) */
/* we can just use the output buffer */
/* HMAC(ki, (seqnum, msg) ) */
#ifdef _SUN_SDK_
#else
#endif /* _SUN_SDK_ */
/* create MAC */
/* copy into output */
/* length of message in network byte order */
/* the message text */
/* the MAC */
return SASL_OK;
}
static int
char *input,
int inputlen,
int seqnum,
unsigned char MAC[16])
{
unsigned int tmpnum;
unsigned short int tmpshort;
int ret;
if (inputlen < 0)
return SASL_FAIL;
/* construct (seqnum, msg) */
/* HMAC(ki, (seqnum, msg) ) */
#ifdef _SUN_SDK_
MAC);
#else
MAC);
#endif /* _SUN_SDK_ */
/* create MAC */
return SASL_OK;
}
static int
{
int result;
return result;
/* make sure the MAC is right */
{
#ifdef _SUN_SDK_
"MAC doesn't match");
return SASL_BADMAC;
#else
return SASL_FAIL;
#endif /* _SUN_SDK_ */
}
text->rec_seqnum++;
/* ok make output message */
bufsize - 15);
return result;
return SASL_OK;
}
static int
const char **input,
unsigned *inputlen,
char **output,
unsigned *outputlen)
{
unsigned int tocopy;
unsigned diff;
int result;
/*
* if less than 4 bytes just copy those we have into text->size
*/
if (*inputlen < 4)
else
tocopy = 4;
return SASL_FAIL; /* too big probably error */
else
}
*outputlen = 0;
if (*inputlen == 0) /* have to wait until next time for data */
return SASL_OK;
return SASL_FAIL;
}
return SASL_FAIL;
*inputlen = 0;
*outputlen = 0;
return SASL_OK;
} else {
}
return result;
/* Reset State */
return SASL_OK;
}
{
int ret;
return ret;
}
static void
{
/* free the stuff in the context */
if (text->enc_in_buf) {
}
}
static void
const sasl_utils_t *utils)
{
if (!reauth) return;
}
}
static void
{
size_t n;
if (!reauth_cache) return;
for (n = 0; n < reauth_cache->size; n++)
}
/***************************** Server Section *****************************/
typedef struct server_context {
static void
const sasl_utils_t * utils,
unsigned char *authorization_id,
unsigned char *pszNonce,
unsigned char *pszCNonce,
{
/* calculate session key */
if (authorization_id != NULL) {
}
/* save HA1 because we need it to make the privacy and integrity keys */
}
const sasl_utils_t * utils,
unsigned char *nonce,
unsigned int ncvalue,
unsigned char *cnonce,
char *qop,
char *digesturi,
char *authorization_id,
char **response_value)
{
char *result;
qop = "auth";
(unsigned char *) authorization_id,
SessionKey,/* H(A1) */
nonce, /* nonce from server */
ncvalue, /* 8 hex digits */
cnonce, /* client nonce */
(unsigned char *) qop, /* qop-value: "", "auth",
* "auth-int" */
(unsigned char *) digesturi, /* requested URL */
(unsigned char *) "AUTHENTICATE",
HEntity, /* H(entity body) if qop="auth-int" */
Response /* request-digest or response-digest */
);
#ifdef _SUN_SDK_
return NULL;
#endif /* _SUN_SDK_ */
/* TODO */
result[HASHHEXLEN] = 0;
/* response_value (used for reauth i think */
if (response_value != NULL) {
SessionKey, /* H(A1) */
nonce, /* nonce from server */
ncvalue, /* 8 hex digits */
cnonce, /* client nonce */
(unsigned char *) qop, /* qop-value: "", "auth",
* "auth-int" */
(unsigned char *) digesturi, /* requested URL */
NULL,
HEntity, /* H(entity body) if qop="auth-int" */
Response /* request-digest or response-digest */
);
if (*response_value == NULL)
return NULL;
(*response_value)[HASHHEXLEN] = 0;
}
return result;
}
static int
char **realm)
{
/* look at user realm first */
} else {
/* Catch improperly converted apps */
#ifdef _SUN_SDK_
"user_realm is an empty string!");
#else
"user_realm is an empty string!");
#endif /* _SUN_SDK_ */
return SASL_BADPARAM;
}
} else {
#ifdef _SUN_SDK_
"no way to obtain domain");
#else
"no way to obtain domain");
#endif /* _SUN_SDK_ */
return SASL_FAIL;
}
return SASL_OK;
}
/*
* Convert hex string to int
*/
{
*res = 0;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
break;
case 'a':
case 'b':
case 'c':
case 'd':
case 'e':
case 'f':
break;
case 'A':
case 'B':
case 'C':
case 'D':
case 'E':
case 'F':
break;
default:
return SASL_BADPARAM;
}
}
return SASL_OK;
}
void **conn_context)
{
/* holds state are in -- allocate server size */
return SASL_NOMEM;
*conn_context = text;
return SASL_OK;
}
static int
const char **serverout,
unsigned *serveroutlen,
{
int result;
char *realm;
unsigned char *nonce;
unsigned resplen;
int added_conf = 0;
"DIGEST-MD5 server step 1");
/* get realm */
/* what options should we offer the client? */
qop[0] = '\0';
cipheropts[0] = '\0';
if (stext->requiressf == 0) {
}
}
#ifdef USE_UEF_SERVER
#else
#endif
/* do we allow this particular cipher? */
if (!added_conf) {
added_conf = 1;
}
#ifdef _SUN_SDK_
sizeof (cipheropts)) {
"internal error: cipheropts too big");
return SASL_FAIL;
}
#endif /* _SUN_SDK_ */
}
cipher++;
}
if (*qop == '\0') {
/* we didn't allow anything?!? we'll return SASL_TOOWEAK, since
that's close enough */
return SASL_TOOWEAK;
}
/*
* digest-challenge = 1#( realm | nonce | qop-options | stale | maxbuf |
* charset | cipher-opts | auth-param )
*/
#ifndef _SUN_SDK_
/* FIXME: get nonce XXX have to clean up after self if fail */
#endif /* !_SUN_SDK_ */
#ifdef _SUN_SDK_
/* Note typo below */
"internal error: failed creating a nonce");
#else
#endif /* _SUN_SDK_ */
return SASL_FAIL;
}
#ifdef _SUN_SDK_
#else
#endif /* _SUN_SDK_ */
#ifdef _SUN_SDK_
return result;
}
#else
#endif /* _SUN_SDK_ */
/* add to challenge; if we chose not to specify a realm, we won't
* send one to the client */
"realm", (unsigned char *) realm,
#ifdef _SUN_SDK_
"internal error: add_to_challenge failed");
#else
#endif /* _SUN_SDK_ */
return SASL_FAIL;
}
/*
* qop-options A quoted string of one or more tokens indicating the
* "quality of protection" values supported by the server. The value
* "auth" indicates authentication; the value "auth-int" indicates
* authentication with integrity protection; the value "auth-conf"
* indicates authentication with integrity protection and encryption.
*/
/* add qop to challenge */
"qop",
#ifdef _SUN_SDK_
"internal error: add_to_challenge 3 failed");
#else
#endif /* _SUN_SDK_ */
return SASL_FAIL;
}
/*
* Cipheropts - list of ciphers server supports
*/
/* add cipher-opts to challenge; only add if there are some */
{
"cipher", (unsigned char *) cipheropts,
#ifdef _SUN_SDK_
"internal error: add_to_challenge 4 failed");
#else
"internal error: add_to_challenge 4 failed");
#endif /* _SUN_SDK_ */
return SASL_FAIL;
}
}
/* "stale" is true if a reauth failed because of a nonce timeout */
#ifdef _SUN_SDK_
"internal error: add_to_challenge failed");
#else
#endif /* _SUN_SDK_ */
return SASL_FAIL;
}
/*
* maxbuf A number indicating the size of the largest buffer the server
* is able to receive when using "auth-int". If this directive is
* missing, the default value is 65536. This directive may appear at most
* once; if multiple instances are present, the client should abort the
* authentication exchange.
*/
"maxbuf",
#ifdef _SUN_SDK_
"internal error: add_to_challenge 5 failed");
#else
"internal error: add_to_challenge 5 failed");
#endif /* _SUN_SDK_ */
return SASL_FAIL;
}
}
"charset",
#ifdef _SUN_SDK_
"internal error: add_to_challenge 6 failed");
#else
#endif /* _SUN_SDK_ */
return SASL_FAIL;
}
/*
* algorithm
* This directive is required for backwards compatibility with HTTP
* Digest., which supports other algorithms. . This directive is
* required and MUST appear exactly once; if not present, or if multiple
* instances are present, the client should abort the authentication
* exchange.
*
* algorithm = "algorithm" "=" "md5-sess"
*/
"algorithm",
#ifdef _SUN_SDK_
"internal error: add_to_challenge 7 failed");
#else
#endif /* _SUN_SDK_ */
return SASL_FAIL;
}
/*
* The size of a digest-challenge MUST be less than 2048 bytes!!!
*/
if (*serveroutlen > 2048) {
#ifdef _SUN_SDK_
"internal error: challenge larger than 2048 bytes");
#else
"internal error: challenge larger than 2048 bytes");
#endif /* _SUN_SDK_ */
return SASL_FAIL;
}
return SASL_CONTINUE;
}
static int
const char *clientin,
unsigned clientinlen,
const char **serverout,
unsigned *serveroutlen,
{
/* verify digest */
int result;
unsigned int noncecount = 0;
/* setting the default value (65536) */
unsigned int n=0;
/* password prop_request */
"*cmusaslsecretDIGEST-MD5",
NULL };
unsigned len;
/* can we mess with clientin? copy it to be safe */
"DIGEST-MD5 server step 2");
#ifdef _SUN_SDK_
if (!in) return SASL_NOMEM;
#endif /* _SUN_SDK_ */
in[clientinlen] = 0;
/* parse what we got */
while (in[0] != '\0') {
break;
/* Extracting parameters */
/*
* digest-response = 1#( username | realm | nonce | cnonce |
* nonce-count | qop | digest-uri | response | maxbuf | charset |
* cipher | auth-param )
*/
#ifdef _SUN_SDK_
"error converting hex to int");
#else
"error converting hex to int");
#endif /* _SUN_SDK_ */
goto FreeAllMem;
}
if (realm) {
#ifdef _SUN_SDK_
"duplicate realm: authentication aborted");
#else
"duplicate realm: authentication aborted");
#endif /* _SUN_SDK_ */
goto FreeAllMem;
}
/*
* digest-uri-value = serv-type "/" host [ "/" serv-name ]
*/
/* verify digest-uri format */
/* make sure it's the service that we're expecting */
#ifdef _SUN_SDK_
"bad digest-uri: doesn't match service");
#else
"bad digest-uri: doesn't match service");
#endif /* _SUN_SDK_ */
goto FreeAllMem;
}
/* xxx we don't verify the hostname component */
maxbuf_count++;
if (maxbuf_count != 1) {
#ifdef _SUN_SDK_
"duplicate maxbuf: authentication aborted");
#else
"duplicate maxbuf: authentication aborted");
#endif /* _SUN_SDK_ */
goto FreeAllMem;
#ifdef _SUN_SDK_
"invalid maxbuf parameter");
#else
#endif /* _SUN_SDK_ */
goto FreeAllMem;
} else {
if (client_maxbuf <= 16) {
#ifdef _SUN_SDK_
"maxbuf parameter too small");
#else
"maxbuf parameter too small");
#endif /* _SUN_SDK_ */
goto FreeAllMem;
}
}
#ifdef _SUN_SDK_
"client doesn't support UTF-8");
#else
#endif /* _SUN_SDK_ */
goto FreeAllMem;
}
} else {
"DIGEST-MD5 unrecognized pair %s/%s: ignoring",
}
}
/*
* username = "username" "=" <"> username-value <">
* username-value = qdstr-val cnonce = "cnonce" "=" <">
* cnonce-value <"> cnonce-value = qdstr-val nonce-count = "nc"
* "=" nc-value nc-value = 8LHEX qop = "qop" "="
* qop-value digest-uri = "digest-uri" "=" digest-uri-value
* digest-uri-value = serv-type "/" host [ "/" serv-name ] serv-type
* = 1*ALPHA host = 1*( ALPHA | DIGIT | "-" | "." ) service
* = host response = "response" "=" <"> response-value <">
* response-value = 32LHEX LHEX = "0" | "1" | "2" | "3" | "4" | "5" |
* "6" | "7" | "8" | "9" | "a" | "b" | "c" | "d" | "e" | "f" cipher =
* "cipher" "=" cipher-value
*/
/* Verifing that all parameters was defined */
(noncecount == 0) ||
#ifdef _SUN_SDK_
"required parameters missing");
#else
#endif /* _SUN_SDK_ */
goto FreeAllMem;
}
/* reauth attempt, see if we have any info for this user */
#ifdef _SUN_SDK_
#else
#endif /* _SUN_SDK_ */
#ifdef _SUN_SDK_
#else
#endif /* _SUN_SDK_ */
}
}
/* we don't have any reauth info, so bail */
goto FreeAllMem;
}
}
/* Sanity check the parameters */
#ifdef _SUN_SDK_
"realm changed: authentication aborted");
#else
"realm changed: authentication aborted");
#endif /* _SUN_SDK_ */
goto FreeAllMem;
}
#ifdef _SUN_SDK_
"nonce changed: authentication aborted");
#else
"nonce changed: authentication aborted");
#endif /* _SUN_SKD_ */
goto FreeAllMem;
}
#ifdef _SUN_SDK_
"incorrect nonce-count: authentication aborted");
#else
"incorrect nonce-count: authentication aborted");
#endif /* _SUN_SDK_ */
goto FreeAllMem;
}
#ifdef _SUN_SDK_
"cnonce changed: authentication aborted");
#else
"cnonce changed: authentication aborted");
#endif /* _SUN_SDK_ */
goto FreeAllMem;
}
#ifdef _SUN_SDK_
"unable to request user password");
#else
#endif /* _SUN_SDK_ */
goto FreeAllMem;
}
/* this will trigger the getting of the aux properties */
/* Note that if we don't have an authorization id, we don't use it... */
#ifdef _SUN_SDK_
"unable canonify user and get auxprops");
#else
#endif /* _SUN_SDK_ */
goto FreeAllMem;
}
if (!authorization_id || !*authorization_id) {
} else {
oparams);
}
#ifdef _SUN_SDK_
"unable to canonicalize authorization ID");
#else
#endif /* _SUN_SDK_ */
goto FreeAllMem;
}
if (result < 0 ||
/* We didn't find this username */
#ifdef _INTEGRATED_SOLARIS_
gettext("no secret in database"));
#else
"no secret in database");
#endif /* _INTEGRATED_SOLARIS_ */
goto FreeAllMem;
}
if (len == 0) {
#ifdef _INTEGRATED_SOLARIS_
gettext("empty secret"));
#else
"empty secret");
#endif /* _INTEGRATED_SOLARIS_ */
goto FreeAllMem;
}
if (!sec) {
#ifdef _SUN_SDK_
"unable to allocate secret");
#else
#endif /* _SUN_SDK_ */
goto FreeAllMem;
}
#ifdef _SUN_SDK_
#else
#endif /* _SUN_SDK_ */
/*
* Verifying response obtained from client
*
* H_URP = H({ username-value,":",realm-value,":",passwd}) sec->data
* contains H_URP
*/
/* Calculate the secret from the plaintext password */
{
#ifdef _SUN_SDK_
#else
#endif /* _SUN_SDK_ */
/*
* A1 = { H( { username-value, ":", realm-value, ":", passwd } ),
* ":", nonce-value, ":", cnonce-value }
*/
}
/* We're done with sec now. Let's get rid of it */
} else {
#ifdef _SUN_SDK_
"Have neither type of secret");
#else
"Have neither type of secret");
#endif /* _SUN_SDK_ */
#ifdef _SUN_SDK_
goto FreeAllMem;
#else
return SASL_FAIL;
#endif /* _SUN_SDK_ */
}
/* defaulting qop to "auth" if not specified */
}
/* see what cipher was requested */
#ifdef USE_UEF_SERVER
#else
#endif
/* find the cipher requested & make sure it's one we're happy
with by policy */
/* found it! */
break;
}
cptr++;
}
n = cptr->n;
} else {
/* erg? client requested something we didn't advertise! */
"protocol violation: client requested invalid cipher");
#ifndef _SUN_SDK_
#endif /* !_SUN_SDK_ */
/* Mark that we attempted security layer negotiation */
goto FreeAllMem;
}
} else {
#ifdef _SUN_SDK_
"protocol violation: client requested invalid qop");
#else
"protocol violation: client requested invalid qop");
#endif /* _SUN_SDK_ */
goto FreeAllMem;
}
qop,
A1,
&text->response_value);
if (serverresponse == NULL) {
#ifndef _SUN_SDK_
#endif /* !_SUN_SDK_ */
result = SASL_NOMEM;
goto FreeAllMem;
}
/* if ok verified */
#ifdef _INTEGRATED_SOLARIS_
gettext("client response doesn't match what we generated"));
#else
"client response doesn't match what we generated");
#endif /* _INTEGRATED_SOLARIS_ */
goto FreeAllMem;
}
/* see if our nonce expired */
#ifdef _INTEGRATED_SOLARIS_
#else
#endif /* _INTEGRATED_SOLARIS_ */
goto FreeAllMem;
}
/*
* nothing more to do; authenticated set oparams information
*/
#ifdef _SUN_SDK_
goto FreeAllMem;
}
#endif
/* MAC block (privacy) */
#ifdef _SUN_SDK_
goto FreeAllMem;
}
#endif
/* MAC block (integrity) */
}
oparams->param_version = 0;
/* used by layers */
/* initialize cipher if need be */
#ifdef _SUN_SDK_
if (text->cipher_init) {
if (text->cipher_free)
"couldn't init cipher");
goto FreeAllMem;
}
}
#else
if (text->cipher_init)
"couldn't init cipher");
}
#endif /* _SUN_SDK_ */
}
/*
* The server receives and validates the "digest-response". The server
* checks that the nonce-count is "00000001". If it supports subsequent
* authentication, it saves the value of the nonce and the nonce-count.
*/
/*
* The "username-value", "realm-value" and "passwd" are encoded according
* to the value of the "charset" directive. If "charset=UTF-8" is
* present, and all the characters of either "username-value" or "passwd"
* are in the ISO 8859-1 character set, then it must be converted to
* UTF-8 before being hashed. A sample implementation of this conversion
* is in section 8.
*/
/* add to challenge */
{
unsigned resplen =
goto FreeAllMem;
}
/* self check */
goto FreeAllMem;
}
}
switch (result) {
case SASL_OK:
/* successful auth, setup for future reauth */
/* successful initial auth, create new entry */
}
/* paranoia. prevent replay attacks */
}
else {
}
break;
default:
/* failed reauth, clear entry */
}
else {
/* failed initial auth, leave existing cache */
}
}
}
/* free everything */
#ifdef _SUN_SDK_
if (authorization_id != NULL)
#endif /* _SUN_SDK_ */
if (serverresponse != NULL)
if (sec)
return result;
}
static int
const char *clientin,
unsigned clientinlen,
const char **serverout,
unsigned *serveroutlen,
{
*serveroutlen = 0;
case 1:
/* setup SSF limits */
stext->requiressf = 0;
} else {
} else {
}
stext->requiressf = 0;
} else {
stext->requiressf =
}
}
/* here's where we attempt fast reauth if possible */
return SASL_OK;
}
#ifdef _SUN_SDK_
"DIGEST-MD5 reauth failed");
#else
"DIGEST-MD5 reauth failed\n");
#endif /* _SUN_SDK_ */
/* re-initialize everything for a fresh start */
/* fall through and issue challenge */
}
case 2:
default:
#ifdef _SUN_SDK_
#else
#endif /* _SUN_SDK_ */
return SASL_FAIL;
}
#ifndef _SUN_SDK_
return SASL_FAIL; /* should never get here */
#endif /* !_SUN_SDK_ */
}
static void
{
}
{
{
"DIGEST-MD5", /* mech_name */
/* EXPORT DELETE START */
#ifdef WITH_RC4
128, /* max_ssf */
112,
#else
/* EXPORT DELETE END */
0,
/* EXPORT DELETE START */
#endif
/* EXPORT DELETE END */
| SASL_SEC_MUTUAL_AUTH, /* security_flags */
SASL_FEAT_ALLOWS_PROXY, /* features */
NULL, /* glob_context */
&digestmd5_server_mech_new, /* mech_new */
&digestmd5_server_mech_step, /* mech_step */
&digestmd5_server_mech_dispose, /* mech_dispose */
&digestmd5_common_mech_free, /* mech_free */
NULL, /* setpass */
NULL, /* user_query */
NULL, /* idle */
NULL, /* mech avail */
NULL /* spare */
}
};
int maxversion,
int *out_version,
int *plugcount)
{
unsigned int len;
int ret;
#endif /* _SUN_SDK_ && USE_UEF */
return SASL_BADVERS;
return ret;
#endif /* _SUN_SDK_ && USE_UEF */
/* reauth cache */
if (reauth_cache == NULL)
return SASL_NOMEM;
/* fetch and canonify the reauth_timeout */
if (timeout)
#ifdef _SUN_SDK_
else
reauth_cache->timeout = 0;
#endif /* _SUN_SDK_ */
if (reauth_cache->timeout < 0)
reauth_cache->timeout = 0;
if (reauth_cache->timeout) {
/* mutex */
if (!reauth_cache->mutex)
return SASL_FAIL;
/* entries */
sizeof(reauth_entry_t));
if (reauth_cache->e == NULL)
return SASL_NOMEM;
}
#ifdef _SUN_SDK_
#ifdef USE_UEF_CLIENT
#endif /* USE_UEF_CLIENT */
#endif /* _SUN_SDK_ */
/* EXPORT DELETE START */
/* CRYPT DELETE START */
#ifdef _INTEGRATED_SOLARIS_
/*
* Let libsasl know that we are a "Sun" plugin so that privacy
* and integrity will be allowed.
*/
#endif /* _INTEGRATED_SOLARIS_ */
/* CRYPT DELETE END */
/* EXPORT DELETE END */
*plugcount = 1;
return SASL_OK;
}
/***************************** Client Section *****************************/
typedef struct client_context {
int protection;
unsigned int server_maxbuf;
#ifdef _INTEGRATED_SOLARIS_
void *h;
#endif /* _INTEGRATED_SOLARIS_ */
/* calculate H(A1) as per spec */
static void
const sasl_utils_t * utils,
unsigned char *pszUserName,
unsigned char *pszRealm,
unsigned char *pszAuthorization_id,
unsigned char *pszNonce,
unsigned char *pszCNonce,
{
(unsigned char *) pszPassword->data,
HA1);
/* calculate the session key */
if (pszAuthorization_id != NULL) {
strlen((char *) pszAuthorization_id));
}
/* xxx rc-* use different n */
/* save HA1 because we'll need it for the privacy and integrity keys */
}
const sasl_utils_t * utils,
unsigned char *username,
unsigned char *realm,
unsigned char *nonce,
unsigned int ncvalue,
unsigned char *cnonce,
char *qop,
unsigned char *digesturi,
unsigned char *authorization_id,
char **response_value)
{
char *result;
/* Verifing that all parameters was defined */
PARAMERROR( utils );
return NULL;
}
/* a NULL realm is equivalent to the empty string */
realm = (unsigned char *) "";
}
/* default to a qop of just authentication */
qop = "auth";
}
SessionKey,/* H(A1) */
nonce, /* nonce from server */
ncvalue, /* 8 hex digits */
cnonce, /* client nonce */
(unsigned char *) qop, /* qop-value: "", "auth",
* "auth-int" */
digesturi, /* requested URL */
(unsigned char *) "AUTHENTICATE",
HEntity, /* H(entity body) if qop="auth-int" */
Response /* request-digest or response-digest */
);
#ifdef _SUN_SDK_
return NULL;
#endif /* _SUN_SDK_ */
result[HASHHEXLEN] = 0;
if (response_value != NULL) {
SessionKey, /* H(A1) */
nonce, /* nonce from server */
ncvalue, /* 8 hex digits */
cnonce, /* client nonce */
(unsigned char *) qop, /* qop-value: "", "auth",
* "auth-int" */
(unsigned char *) digesturi, /* requested URL */
NULL,
HEntity, /* H(entity body) if qop="auth-int" */
Response /* request-digest or response-digest */
);
#ifdef _SUN_SDK_
if (*response_value != NULL)
#endif /* _SUN_SDK_ */
if (*response_value == NULL)
return NULL;
(*response_value)[HASHHEXLEN] = 0;
}
return result;
}
static int
{
unsigned nbits = 0;
unsigned resplen = 0;
int result;
switch (ctext->protection) {
case DIGEST_PRIVACY:
qop = "auth-conf";
break;
case DIGEST_INTEGRITY:
qop = "auth-int";
break;
case DIGEST_NOLAYER:
default:
qop = "auth";
}
1);
result = SASL_NOMEM;
goto FreeAllocatedMem;
};
/* allocated exactly this. safe */
/*
* strcat (digesturi, "/"); strcat (digesturi, params->serverFQDN);
*/
/* response */
response =
#ifdef _SUN_SDK_
#else
#endif /* _SUN_SDK_ */
qop,
#ifdef _SUN_SDK_
#else
#endif /* _SUN_SDK_ */
&text->response_value);
#ifdef _SUN_SDK_
result = SASL_NOMEM;
goto FreeAllocatedMem;
}
#endif /* _SUN_SDK_ */
&(text->out_buf_len),
resplen);
goto FreeAllocatedMem;
}
#ifdef _SUN_SDK_
#else
#endif /* _SUN_SDK_ */
goto FreeAllocatedMem;
}
}
goto FreeAllocatedMem;
}
goto FreeAllocatedMem;
}
goto FreeAllocatedMem;
}
goto FreeAllocatedMem;
}
"cipher",
goto FreeAllocatedMem;
}
}
"maxbuf", (unsigned char *) maxbufstr,
#ifdef _SUN_SDK_
"internal error: add_to_challenge maxbuf failed");
#else
"internal error: add_to_challenge maxbuf failed");
#endif /* _SUN_SDK_ */
goto FreeAllocatedMem;
}
}
if (IsUTF8) {
"charset", (unsigned char *) "utf-8",
goto FreeAllocatedMem;
}
}
goto FreeAllocatedMem;
}
"response", (unsigned char *) response,
goto FreeAllocatedMem;
}
/* self check */
goto FreeAllocatedMem;
}
/* set oparams */
#ifdef _SUN_SDK_
#else
#endif /* _SUN_SDK_ */
#ifdef _SUN_SDK_
return (SASL_BADPARAM);
#endif
/* MAC block (privacy) */
#ifdef _SUN_SDK_
return (SASL_BADPARAM);
#endif
/* MAC block (integrity) */
}
/* used by layers */
/* initialize cipher if need be */
#ifdef _SUN_SDK_
if (text->cipher_init) {
if (text->cipher_free)
"couldn't init cipher");
goto FreeAllocatedMem;
}
}
#else
if (text->cipher_init)
#endif /* _SUN_SDK_ */
}
return result;
}
const char *serverin, unsigned serverinlen,
{
int nrealm = 0;
int protection = 0;
int ciphers = 0;
int maxbuf_count = 0;
#ifndef _SUN_SDK_
#endif /* !_SUN_SDK_ */
int algorithm_count = 0;
if (!serverin || !serverinlen) {
#ifndef _SUN_SDK_
"no server challenge");
#else
"no server challenge");
#endif /* _SUN_SDK_ */
return SASL_FAIL;
}
in[serverinlen] = 0;
/* create a new cnonce */
#ifdef _SUN_SDK_
"failed to create cnonce");
#else
"failed to create cnonce");
#endif /* _SUN_SDK_ */
goto FreeAllocatedMem;
}
/* parse the challenge */
while (in[0] != '\0') {
/* if parse error */
#ifdef _SUN_SDK_
"Parse error");
#else
#endif /* _SUN_SDK_ */
goto FreeAllocatedMem;
}
nrealm++;
if(!realms)
else
sizeof(char *) * (nrealm + 1));
result = SASL_NOMEM;
goto FreeAllocatedMem;
}
NULL);
*comma++ = '\0';
}
} else {
"Server supports unknown layer: %s\n",
value);
}
}
if (protection == 0) {
#ifdef _INTEGRATED_SOLARIS_
gettext("Server doesn't support known qop level"));
#else
"Server doesn't support known qop level");
#endif /* _INTEGRATED_SOLARIS_ */
goto FreeAllocatedMem;
}
#ifdef USE_UEF_CLIENT
#else
#endif
*comma++ = '\0';
}
/* do we support this cipher? */
cipher++;
}
} else {
"Server supports unknown cipher: %s\n",
value);
}
}
/* clear any cached password */
if (ctext->free_password)
/* maxbuf A number indicating the size of the largest
* buffer the server is able to receive when using
* "auth-int". If this directive is missing, the default
* value is 65536. This directive may appear at most once;
* if multiple instances are present, the client should
* abort the authentication exchange.
*/
maxbuf_count++;
if (maxbuf_count != 1) {
#ifdef _SUN_SDK_
"At least two maxbuf directives found."
" Authentication aborted");
#else
"At least two maxbuf directives found. Authentication aborted");
#endif /* _SUN_SDK_ */
goto FreeAllocatedMem;
#ifdef _SUN_SDK_
"Invalid maxbuf parameter received from server");
#else
"Invalid maxbuf parameter received from server");
#endif /* _SUN_SDK_ */
goto FreeAllocatedMem;
} else {
#ifdef _SUN_SDK_
"Invalid maxbuf parameter received from server"
" (too small: %s)", value);
#else
"Invalid maxbuf parameter received from server (too small: %s)", value);
#endif /* _SUN_SDK_ */
goto FreeAllocatedMem;
}
}
#ifdef _SUN_SDK_
"Charset must be UTF-8");
#else
"Charset must be UTF-8");
#endif /* _SUN_SDK_ */
goto FreeAllocatedMem;
} else {
#ifndef _SUN_SDK_
#endif /* !_SUN_SDK_ */
}
{
#ifdef _SUN_SDK_
"'algorithm' isn't 'md5-sess'");
#else
"'algorithm' isn't 'md5-sess'");
#endif /* _SUN_SDK_ */
goto FreeAllocatedMem;
}
if (algorithm_count > 1)
{
#ifdef _SUN_SDK_
"Must see 'algorithm' only once");
#else
"Must see 'algorithm' only once");
#endif /* _SUN_SDK_ */
goto FreeAllocatedMem;
}
} else {
"DIGEST-MD5 unrecognized pair %s/%s: ignoring",
}
}
if (algorithm_count != 1) {
#ifdef _SUN_SDK_
"Must see 'algorithm' once. Didn't see at all");
#else
"Must see 'algorithm' once. Didn't see at all");
#endif /* _SUN_SDK_ */
goto FreeAllocatedMem;
}
/* make sure we have everything we require */
#ifdef _SUN_SDK_
"Don't have nonce.");
#else
"Don't have nonce.");
#endif /* _SUN_SDK_ */
goto FreeAllocatedMem;
}
/* get requested ssf */
/* what do we _need_? how much is too much? */
musthave = 0;
limit = 0;
} else {
} else {
limit = 0;
}
} else {
musthave = 0;
}
}
/* we now go searching for an option that gives us at least "musthave"
and at most "limit" bits of ssf. */
/* let's find an encryption scheme that we like */
#ifdef USE_UEF_CLIENT
#else
#endif
/* examine each cipher we support, see if it meets our security
requirements, and see if the server supports it.
choose the best one of these */
}
cipher++;
}
/* we found a cipher we like */
} else {
/* we didn't find any ciphers we like */
#ifdef _INTEGRATED_SOLARIS_
gettext("No good privacy layers"));
#else
"No good privacy layers");
#endif /* _INTEGRATED_SOLARIS_ */
}
}
/* we failed to find an encryption layer we liked;
can we use integrity or nothing? */
&& (protection & DIGEST_INTEGRITY)) {
/* integrity */
#ifdef _SUN_SDK_
} else if (musthave == 0) {
#else
} else if (musthave <= 0) {
#endif /* _SUN_SDK_ */
/* no layer */
/* See if server supports not having a layer */
#ifdef _INTEGRATED_SOLARIS_
gettext("Server doesn't support \"no layer\""));
#else
"Server doesn't support \"no layer\"");
#endif /* _INTEGRATED_SOLARIS_ */
goto FreeAllocatedMem;
}
} else {
#ifdef _INTEGRATED_SOLARIS_
gettext("Can't find an acceptable layer"));
#else
"Can't find an acceptable layer");
#endif /* _INTEGRATED_SOLARIS_ */
goto FreeAllocatedMem;
}
}
int lup;
/* need to free all the realms */
}
return result;
}
{
/* try to get the authid */
return auth_result;
}
}
/* try to get the userid */
return user_result;
}
}
/* try to get the password */
return pass_result;
}
}
/* try to get the realm */
if (realms) {
if(nrealm == 1) {
/* only one choice */
} else {
/* ask the user */
(const char **) realms,
(const char **) &realm,
}
}
/* fake the realm if we must */
if (params->serverFQDN) {
} else {
return realm_result;
}
}
}
/* free prompts we got */
if (prompt_need && *prompt_need) {
*prompt_need = NULL;
}
/* if there are prompts not filled in */
/* make our default realm */
if (realm_chal) {
} else {
return SASL_NOMEM;
}
}
/* make the prompt list */
result =
#if defined _INTEGRATED_SOLARIS_
user_result == SASL_INTERACT ?
gettext("Please enter your authorization name"))
: NULL,
NULL,
auth_result == SASL_INTERACT ?
gettext("Please enter your authentication name"))
: NULL,
NULL,
pass_result == SASL_INTERACT ?
gettext("Please enter your password"))
#else
user_result == SASL_INTERACT ?
"Please enter your authorization name" : NULL,
NULL,
auth_result == SASL_INTERACT ?
"Please enter your authentication name" : NULL,
NULL,
pass_result == SASL_INTERACT ?
"Please enter your realm" : NULL,
#endif /* _INTEGRATED_SOLARIS_ */
return result;
}
oparams);
}
else {
}
}
/* Get an allocated version of the realm into the structure */
}
return result;
}
static int
void **conn_context)
{
/* holds state are in -- allocate client size */
return SASL_NOMEM;
*conn_context = text;
return SASL_OK;
}
static int
const char **clientout,
unsigned *clientoutlen,
{
unsigned val;
"DIGEST-MD5 client step 1");
/* check if we have cached info for this user on this server */
params->serverFQDN) &&
#ifdef _SUN_SDK_
#endif /* _SUN_SDK_ */
/* we have info, so use it */
#ifdef _SUN_SDK_
#else
#endif /* _SUN_SDK_ */
#ifdef _SUN_SDK_
#else
#endif /* _SUN_SDK_ */
}
}
/* we don't have any reauth info, so just return
* that there is no initial client send */
return SASL_CONTINUE;
}
/*
* (username | realm | nonce | cnonce | nonce-count | qop digest-uri |
* response | maxbuf | charset | auth-param )
*/
return SASL_CONTINUE;
}
static int
const char *serverin,
unsigned serverinlen,
const char **clientout,
unsigned *clientoutlen,
{
int nrealm = 0;
"DIGEST-MD5 client step 2");
return SASL_BADPARAM;
}
/* don't bother parsing the challenge more than once */
if (nrealm == 1) {
/* only one choice! */
/* free realms */
}
}
/*
* (username | realm | nonce | cnonce | nonce-count | qop digest-uri |
* response | maxbuf | charset | auth-param )
*/
if (realms) {
int lup;
/* need to free all the realms */
}
return result;
}
static int
const char *serverin,
unsigned serverinlen,
{
char *in_start;
"DIGEST-MD5 client step 3");
/* Verify that server is really what he claims to be */
in[serverinlen] = 0;
/* parse the response */
while (in[0] != '\0') {
#ifdef _SUN_SDK_
"DIGEST-MD5 Received Garbage");
#else
"DIGEST-MD5 Received Garbage");
#endif /* _SUN_SDK_ */
break;
}
#ifdef _INTEGRATED_SOLARIS_
gettext("Server authentication failed"));
#else
"DIGEST-MD5: This server wants us to believe that he knows shared secret");
#endif /* _INTEGRATED_SOLARIS_ */
} else {
oparams->param_version = 0;
}
break;
} else {
"DIGEST-MD5 unrecognized pair %s/%s: ignoring",
}
}
switch (result) {
case SASL_OK:
/* successful initial auth, setup for future reauth */
}
#ifndef _SUN_SDK_
else {
/* reauth, we already incremented nonce_count */
}
#endif /* !_SUN_SDK_ */
break;
default:
/* failed reauth, clear cache */
}
else {
/* failed initial auth, leave existing cache */
}
}
}
return result;
}
static int
const char *serverin,
unsigned serverinlen,
const char **clientout,
unsigned *clientoutlen,
{
*clientoutlen = 0;
case 1:
if (!serverin) {
/* here's where we attempt fast reauth if possible */
int reauth = 0;
/* check if we have saved info for this server */
params->serverFQDN);
}
if (reauth) {
oparams);
}
else {
/* we don't have any reauth info, so just return
* that there is no initial client send */
return SASL_CONTINUE;
}
}
/* fall through and respond to challenge */
case 3:
oparams);
}
/* fall through and respond to challenge */
/* cleanup after a failed reauth attempt */
}
#ifdef _SUN_SDK_
#else
#endif /* _SUN_SDK_ */
case 2:
oparams);
default:
#ifdef _SUN_SDK_
#else
#endif /* _SUN_SDK_ */
return SASL_FAIL;
}
return SASL_FAIL; /* should never get here */
}
static void
{
#ifdef _INTEGRATED_SOLARIS_
#endif /* _INTEGRATED_SOLARIS_ */
}
{
{
"DIGEST-MD5",
/* EXPORT DELETE START */
#ifdef WITH_RC4 /* mech_name */
128, /* max ssf */
112,
#else
/* EXPORT DELETE END */
0,
/* EXPORT DELETE START */
#endif
/* EXPORT DELETE END */
| SASL_SEC_MUTUAL_AUTH, /* security_flags */
SASL_FEAT_ALLOWS_PROXY, /* features */
NULL, /* required_prompts */
NULL, /* glob_context */
&digestmd5_client_mech_new, /* mech_new */
&digestmd5_client_mech_step, /* mech_step */
&digestmd5_client_mech_dispose, /* mech_dispose */
&digestmd5_common_mech_free, /* mech_free */
NULL, /* idle */
NULL, /* spare1 */
NULL /* spare2 */
}
};
int maxversion,
int *out_version,
int *plugcount)
{
int ret;
#endif /* _SUN_SDK_ && USE_UEF */
return SASL_BADVERS;
return ret;
#endif /* _SUN_SDK_ && USE_UEF */
/* reauth cache */
if (reauth_cache == NULL)
return SASL_NOMEM;
/* mutex */
if (!reauth_cache->mutex)
return SASL_FAIL;
/* entries */
sizeof(reauth_entry_t));
if (reauth_cache->e == NULL)
return SASL_NOMEM;
#ifdef _SUN_SDK_
#ifdef USE_UEF_CLIENT
#endif /* USE_UEF_CLIENT */
#endif /* _SUN_SDK_ */
/* EXPORT DELETE START */
/* CRYPT DELETE START */
#ifdef _INTEGRATED_SOLARIS_
/*
* Let libsasl know that we are a "Sun" plugin so that privacy
* and integrity will be allowed.
*/
#endif /* _INTEGRATED_SOLARIS_ */
/* CRYPT DELETE END */
/* EXPORT DELETE END */
*plugcount = 1;
return SASL_OK;
}
#ifdef _SUN_SDK_
#ifdef USE_UEF
/* If we fail here - we should just not offer privacy or integrity */
static int
{
int i, m;
#ifdef DEBUG
#endif
return SASL_FAIL;
}
return SASL_NOMEM;
#ifdef DEBUG
#endif
return SASL_FAIL;
}
for (i = 0; i < ulSlotCount; i++) {
#ifdef DEBUG
"C_GetMechanismList returned 0x%.8X count:%d\n", rv,
#endif
return SASL_FAIL;
}
if (pMechTypeList == NULL_PTR) {
return SASL_NOMEM;
}
#ifdef DEBUG
"C_GetMechanismList returned 0x%.8X count:%d\n", rv,
#endif
return SASL_FAIL;
}
for (m = 0; m < ulMechTypeCount; m++) {
if (pMechTypeList[m] == mech_type)
break;
}
if (m < ulMechTypeCount)
break;
}
if (i < ulSlotCount) {
return SASL_OK;
}
return SASL_FAIL;
}
static int
{
int got_rc4;
int got_des;
int got_3des;
int next_c;
if (got_uef_slot)
return (SASL_OK);
if (LOCK_MUTEX(&uef_init_mutex) < 0)
return (SASL_FAIL);
#ifdef DEBUG
"C_Initialize returned 0x%.8X\n", rv);
#endif
return SASL_FAIL;
}
if (!got_rc4)
if (!got_des)
if (!got_3des)
/* adjust the available ciphers */
if (got_des) {
next_c++;
}
if (got_3des) {
next_c++;
}
got_uef_slot = TRUE;
return (SASL_OK);
}
#endif /* USE_UEF */
#endif /* _SUN_SDK_ */