diff options
| author | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
|---|---|---|
| committer | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
| commit | 7c478bd95313f5f23a4c958a745db2134aa03244 (patch) | |
| tree | c871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/lib/sasl_plugins | |
| download | illumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz | |
OpenSolaris Launch
Diffstat (limited to 'usr/src/lib/sasl_plugins')
67 files changed, 10050 insertions, 0 deletions
diff --git a/usr/src/lib/sasl_plugins/Makefile b/usr/src/lib/sasl_plugins/Makefile new file mode 100644 index 0000000000..75276acb75 --- /dev/null +++ b/usr/src/lib/sasl_plugins/Makefile @@ -0,0 +1,83 @@ +# +# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +# Note, to build SASL msg file go to $SRC/lib/libsasl and make _msg +# target there. Messages in sasl_plugins will be picked up from there. + +include ../Makefile.lib + +SUBDIRS = cram digestmd5 gssapi plain + +all := TARGET= all +clean := TARGET= clean +clobber := TARGET= clobber +install := TARGET= install +lint := TARGET= lint + +.KEEP_STATE: + +all clean clobber install: $(SUBDIRS) + +lint: $(SUBDIRS) + +install_h check: + +$(SUBDIRS) spec: FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: + +# EXPORT DELETE START +# CRYPT DELETE START +# Special target to clean up the source tree for export distribution +# Warning: This target changes the source tree +EXPORT_SRC: + $(RM) Makefile+ \ + digestmd5/digestmd5.c+ \ + gssapi/gssapi.c+ + + $(SED) -e "/^# EXPORT DELETE START/,/^# EXPORT DELETE END/d" \ + < Makefile > Makefile+ + $(MV) Makefile+ Makefile + + $(SED) -e "/EXPORT DELETE START/,/EXPORT DELETE END/d" \ + < digestmd5/digestmd5.c > digestmd5/digestmd5.c+ + $(MV) digestmd5/digestmd5.c+ digestmd5/digestmd5.c + + $(SED) -e "/EXPORT DELETE START/,/EXPORT DELETE END/d" \ + < gssapi/gssapi.c > gssapi/gssapi.c+ + $(MV) gssapi/gssapi.c+ gssapi/gssapi.c + + $(CHMOD) 444 \ + Makefile \ + digestmd5/digestmd5.c \ + gssapi/gssapi.c + +CRYPT_SRC: + $(RM) Makefile+ + + $(SED) -e "/CRYPT DELETE START/,/CRYPT DELETE END/d" \ + < digestmd5/digestmd5.c | $(SED) -e "/EXPORT DELETE/d" \ + > digestmd5/digestmd5.c+ + $(MV) digestmd5/digestmd5.c+ digestmd5/digestmd5.c + + $(SED) -e "/CRYPT DELETE START/,/CRYPT DELETE END/d" \ + < gssapi/gssapi.c | $(SED) -e "/EXPORT DELETE/d" \ + > gssapi/gssapi.c+ + $(MV) gssapi/gssapi.c+ gssapi/gssapi.c + + $(SED) -e "/^# CRYPT DELETE START/,/^# CRYPT DELETE END/d" \ + < Makefile | $(SED) -e "/^# EXPORT DELETE/d" > Makefile+ + $(MV) Makefile+ Makefile + $(CHMOD) 444 Makefile digestmd5/digestmd5.c gssapi/gssapi.c + +# CRYPT DELETE END +# EXPORT DELETE END + +include ../Makefile.targ + +.PARALLEL: $(SUBDIRS) diff --git a/usr/src/lib/sasl_plugins/Makefile.com b/usr/src/lib/sasl_plugins/Makefile.com new file mode 100644 index 0000000000..4482f21022 --- /dev/null +++ b/usr/src/lib/sasl_plugins/Makefile.com @@ -0,0 +1,42 @@ +# +# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +COMMONOBJS= plugin_common.o +OBJECTS= $(PLUG_OBJS) $(COMMONOBJS) + +include $(SRC)/lib/Makefile.lib + +CPPFLAGS += -I$(SRC)/lib/libsasl/include + +LIBS = $(DYNLIB) +SRCS= $(PLUG_OBJS:%.o=../%.c) $(COMMONOBJS:%.o=../../%.c) +LDLIBS += -lsocket -lc $(PLUG_LIBS) +SRCDIR= .. + +MAPDIR= ../spec/$(TRANSMACH) +SPECMAPFILE= $(MAPDIR)/mapfile + +ROOTLIBDIR= $(ROOT)/usr/lib/sasl +ROOTLIBDIR64= $(ROOT)/usr/lib/sasl/$(MACH64) + +LINTFLAGS= $(ENC_FLAGS) +LINTFLAGS64= -Xarch=$(MACH64:sparcv9=v9) $(ENC_FLAGS) + +CFLAGS += $(CCVERBOSE) $(XSTRCONST) $(ENC_FLAGS) +CFLAGS64 += $(XSTRCONST) $(ENC_FLAGS) + +.KEEP_STATE: + +all: $(LIBS) + +lint: lintcheck + +pics/%.o: $(SRC)/lib/libsasl/plugin/%.c + $(COMPILE.c) -o $@ $< + $(POST_PROCESS_O) + +include $(SRC)/lib/Makefile.targ diff --git a/usr/src/lib/sasl_plugins/Makefile.subdirs b/usr/src/lib/sasl_plugins/Makefile.subdirs new file mode 100644 index 0000000000..cba9aa690b --- /dev/null +++ b/usr/src/lib/sasl_plugins/Makefile.subdirs @@ -0,0 +1,27 @@ +# +# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +include $(SRC)/lib/Makefile.lib + +SUBDIRS = $(MACH) +$(BUILD64)SUBDIRS += $(MACH64) + +all := TARGET= all +clean := TARGET= clean +clobber := TARGET= clobber +install := TARGET= install +lint := TARGET= lint +.KEEP_STATE: + +all clean clobber install: spec .WAIT $(SUBDIRS) + +lint: $(SUBDIRS) + +$(SUBDIRS) spec: FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: diff --git a/usr/src/lib/sasl_plugins/cram/Makefile b/usr/src/lib/sasl_plugins/cram/Makefile new file mode 100644 index 0000000000..18b5b0cf5d --- /dev/null +++ b/usr/src/lib/sasl_plugins/cram/Makefile @@ -0,0 +1,10 @@ +# +# Copyright 2003 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +# lib/libsasl/plugins/cram/Makefile +# + +include ../Makefile.subdirs diff --git a/usr/src/lib/sasl_plugins/cram/Makefile.com b/usr/src/lib/sasl_plugins/cram/Makefile.com new file mode 100644 index 0000000000..1cda7ac555 --- /dev/null +++ b/usr/src/lib/sasl_plugins/cram/Makefile.com @@ -0,0 +1,13 @@ +# +# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" + +LIBRARY= crammd5.a +VERS= .1 + +PLUG_OBJS= cram.o crammd5_init.o + +# include common definitions +include ../../Makefile.com diff --git a/usr/src/lib/sasl_plugins/cram/amd64/Makefile b/usr/src/lib/sasl_plugins/cram/amd64/Makefile new file mode 100644 index 0000000000..13ae0db12d --- /dev/null +++ b/usr/src/lib/sasl_plugins/cram/amd64/Makefile @@ -0,0 +1,11 @@ +# +# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +include ../Makefile.com +include ../../../Makefile.lib.64 + +install: all $(ROOTLIBS64) diff --git a/usr/src/lib/sasl_plugins/cram/cram.c b/usr/src/lib/sasl_plugins/cram/cram.c new file mode 100644 index 0000000000..7d4f7b430e --- /dev/null +++ b/usr/src/lib/sasl_plugins/cram/cram.c @@ -0,0 +1,801 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +#pragma ident "%Z%%M% %I% %E% SMI" + +/* CRAM-MD5 SASL plugin + * Rob Siemborski + * Tim Martin + * $Id: cram.c,v 1.79 2003/02/18 18:27:37 rjs3 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 + * the documentation and/or other materials provided with the + * 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 <string.h> +#include <stdlib.h> +#include <stdio.h> +#ifndef macintosh +#include <sys/stat.h> +#endif +#include <fcntl.h> + +#include <sasl.h> +#include <saslplug.h> +#include <saslutil.h> + +#ifdef _SUN_SDK_ +#include <unistd.h> +#endif /* _SUN_SDK_ */ + +#include "plugin_common.h" + +#ifdef macintosh +#include <sasl_cram_plugin_decl.h> +#endif + +/***************************** Common Section *****************************/ + +#ifndef _SUN_SDK_ +static const char plugin_id[] = "$Id: cram.c,v 1.79 2003/02/18 18:27:37 rjs3 Exp $"; +#endif /* !_SUN_SDK_ */ + +/* convert a string of 8bit chars to it's representation in hex + * using lowercase letters + */ +static char *convert16(unsigned char *in, int inlen, const sasl_utils_t *utils) +{ + static char hex[]="0123456789abcdef"; + int lup; + char *out; + + out = utils->malloc(inlen*2+1); + if (out == NULL) return NULL; + + for (lup=0; lup < inlen; lup++) { + out[lup*2] = hex[in[lup] >> 4]; + out[lup*2+1] = hex[in[lup] & 15]; + } + + out[lup*2] = 0; + return out; +} + + +/***************************** Server Section *****************************/ + +typedef struct server_context { + int state; + + char *challenge; +} server_context_t; + +static int +crammd5_server_mech_new(void *glob_context __attribute__((unused)), + sasl_server_params_t *sparams, + const char *challenge __attribute__((unused)), + unsigned challen __attribute__((unused)), + void **conn_context) +{ + server_context_t *text; + + /* holds state are in */ + text = sparams->utils->malloc(sizeof(server_context_t)); + if (text == NULL) { + MEMERROR( sparams->utils ); + return SASL_NOMEM; + } + + memset(text, 0, sizeof(server_context_t)); + + text->state = 1; + + *conn_context = text; + + return SASL_OK; +} + +/* + * Returns the current time (or part of it) in string form + * maximum length=15 + */ +static char *gettime(sasl_server_params_t *sparams) +{ + char *ret; + time_t t; + + t=time(NULL); + ret= sparams->utils->malloc(15); + if (ret==NULL) return NULL; + + /* the bottom bits are really the only random ones so if + we overflow we don't want to loose them */ + snprintf(ret,15,"%lu",t%(0xFFFFFF)); + + return ret; +} + +static char *randomdigits(sasl_server_params_t *sparams) +{ + unsigned int num; + char *ret; + unsigned char temp[5]; /* random 32-bit number */ + +#if defined _DEV_URANDOM && defined _SUN_SDK_ + { + int fd = open(_DEV_URANDOM, O_RDONLY); + int nread = 0; + + if (fd != -1) { + nread = read(fd, temp, 4); + close(fd); + } + if (nread != 4) + sparams->utils->rand(sparams->utils->rpool, + (char *) temp, 4); + } +#else + sparams->utils->rand(sparams->utils->rpool,(char *) temp,4); +#endif /* _DEV_URANDOM && _SUN_SDK_ */ + num=(temp[0] * 256 * 256 * 256) + + (temp[1] * 256 * 256) + + (temp[2] * 256) + + (temp[3] ); + + ret = sparams->utils->malloc(15); /* there's no way an unsigned can be longer than this right? */ + if (ret == NULL) return NULL; + sprintf(ret, "%u", num); + + return ret; +} + +static int +crammd5_server_mech_step1(server_context_t *text, + sasl_server_params_t *sparams, + const char *clientin __attribute__((unused)), + unsigned clientinlen, + const char **serverout, + unsigned *serveroutlen, + sasl_out_params_t *oparams __attribute__((unused))) +{ + char *time, *randdigits; + + /* we shouldn't have received anything */ + if (clientinlen != 0) { +#ifdef _SUN_SDK_ + sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR, + "CRAM-MD5 does not accept inital data"); +#else + SETERROR(sparams->utils, "CRAM-MD5 does not accpet inital data"); +#endif /* _SUN_SDK_ */ + return SASL_BADPROT; + } + + /* get time and a random number for the nonce */ + time = gettime(sparams); + randdigits = randomdigits(sparams); + if ((time == NULL) || (randdigits == NULL)) { + MEMERROR( sparams->utils ); + return SASL_NOMEM; + } + + /* allocate some space for the challenge */ + text->challenge = sparams->utils->malloc(200 + 1); + if (text->challenge == NULL) { + MEMERROR(sparams->utils); + return SASL_NOMEM; + } + + /* create the challenge */ + snprintf(text->challenge, 200, "<%s.%s@%s>", randdigits, time, + sparams->serverFQDN); + + *serverout = text->challenge; + *serveroutlen = strlen(text->challenge); + + /* free stuff */ + sparams->utils->free(time); + sparams->utils->free(randdigits); + + text->state = 2; + + return SASL_CONTINUE; +} + +static int +crammd5_server_mech_step2(server_context_t *text, + sasl_server_params_t *sparams, + const char *clientin, + unsigned clientinlen, + const char **serverout __attribute__((unused)), + unsigned *serveroutlen __attribute__((unused)), + sasl_out_params_t *oparams) +{ + char *userid = NULL; + sasl_secret_t *sec = NULL; + int pos, len; + int result = SASL_FAIL; + const char *password_request[] = { SASL_AUX_PASSWORD, + "*cmusaslsecretCRAM-MD5", + NULL }; + struct propval auxprop_values[3]; + HMAC_MD5_CTX tmphmac; + HMAC_MD5_STATE md5state; + int clear_md5state = 0; + char *digest_str = NULL; + UINT4 digest[4]; + + /* extract userid; everything before last space */ + pos = clientinlen-1; + while ((pos > 0) && (clientin[pos] != ' ')) pos--; + + if (pos <= 0) { +#ifdef _SUN_SDK_ + sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR, + "need authentication name"); +#else + SETERROR( sparams->utils,"need authentication name"); +#endif /* _SUN_SDK_ */ + return SASL_BADPROT; + } + + userid = (char *) sparams->utils->malloc(pos+1); + if (userid == NULL) { + MEMERROR( sparams->utils); + return SASL_NOMEM; + } + + /* copy authstr out */ + memcpy(userid, clientin, pos); + userid[pos] = '\0'; + + result = sparams->utils->prop_request(sparams->propctx, password_request); + if (result != SASL_OK) goto done; + + /* this will trigger the getting of the aux properties */ + result = sparams->canon_user(sparams->utils->conn, + userid, 0, SASL_CU_AUTHID | SASL_CU_AUTHZID, + oparams); + if (result != SASL_OK) goto done; + + result = sparams->utils->prop_getnames(sparams->propctx, + password_request, + auxprop_values); + if (result < 0 || + ((!auxprop_values[0].name || !auxprop_values[0].values) && + (!auxprop_values[1].name || !auxprop_values[1].values))) { + /* We didn't find this username */ +#ifdef _INTEGRATED_SOLARIS_ + sparams->utils->seterror(sparams->utils->conn,0, + gettext("no secret in database")); +#else + sparams->utils->seterror(sparams->utils->conn,0, + "no secret in database"); +#endif /* _INTEGRATED_SOLARIS_ */ + result = SASL_NOUSER; + goto done; + } + + if (auxprop_values[0].name && auxprop_values[0].values) { + len = strlen(auxprop_values[0].values[0]); + if (len == 0) { +#ifdef _INTEGRATED_SOLARIS_ + sparams->utils->seterror(sparams->utils->conn,0, + gettext("empty secret")); +#else + sparams->utils->seterror(sparams->utils->conn,0, + "empty secret"); +#endif /* _INTEGRATED_SOLARIS_ */ + result = SASL_FAIL; + goto done; + } + + sec = sparams->utils->malloc(sizeof(sasl_secret_t) + len); + if (!sec) goto done; + + sec->len = len; +#ifdef _SUN_SDK_ + strncpy((char *)sec->data, auxprop_values[0].values[0], len + 1); +#else + strncpy(sec->data, auxprop_values[0].values[0], len + 1); +#endif /* _SUN_SDK_ */ + + clear_md5state = 1; + /* Do precalculation on plaintext secret */ + sparams->utils->hmac_md5_precalc(&md5state, /* OUT */ + sec->data, + sec->len); + } else if (auxprop_values[1].name && auxprop_values[1].values) { + /* We have a precomputed secret */ + memcpy(&md5state, auxprop_values[1].values[0], + sizeof(HMAC_MD5_STATE)); + } else { +#ifdef _SUN_SDK_ + sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR, + "Have neither type of secret"); +#else + sparams->utils->seterror(sparams->utils->conn, 0, + "Have neither type of secret"); +#endif /* _SUN_SDK_ */ + return SASL_FAIL; + } + + /* ok this is annoying: + so we have this half-way hmac transform instead of the plaintext + that means we half to: + -import it back into a md5 context + -do an md5update with the nonce + -finalize it + */ + sparams->utils->hmac_md5_import(&tmphmac, (HMAC_MD5_STATE *) &md5state); + sparams->utils->MD5Update(&(tmphmac.ictx), + (const unsigned char *) text->challenge, + strlen(text->challenge)); + sparams->utils->hmac_md5_final((unsigned char *) &digest, &tmphmac); + + /* convert to base 16 with lower case letters */ + digest_str = convert16((unsigned char *) digest, 16, sparams->utils); + + /* if same then verified + * - we know digest_str is null terminated but clientin might not be + */ + if (strncmp(digest_str, clientin+pos+1, strlen(digest_str)) != 0) { +#ifdef _INTEGRATED_SOLARIS_ + sparams->utils->seterror(sparams->utils->conn, 0, + gettext("incorrect digest response")); +#else + sparams->utils->seterror(sparams->utils->conn, 0, + "incorrect digest response"); +#endif /* _INTEGRATED_SOLARIS_ */ + result = SASL_BADAUTH; + goto done; + } + + /* set oparams */ + oparams->doneflag = 1; + oparams->mech_ssf = 0; + oparams->maxoutbuf = 0; + oparams->encode_context = NULL; + oparams->encode = NULL; + oparams->decode_context = NULL; + oparams->decode = NULL; + oparams->param_version = 0; + + result = SASL_OK; + + done: + if (userid) sparams->utils->free(userid); + if (sec) _plug_free_secret(sparams->utils, &sec); + + if (digest_str) sparams->utils->free(digest_str); + if (clear_md5state) memset(&md5state, 0, sizeof(md5state)); + + return result; +} + +static int crammd5_server_mech_step(void *conn_context, + sasl_server_params_t *sparams, + const char *clientin, + unsigned clientinlen, + const char **serverout, + unsigned *serveroutlen, + sasl_out_params_t *oparams) +{ + server_context_t *text = (server_context_t *) conn_context; + + *serverout = NULL; + *serveroutlen = 0; + + /* this should be well more than is ever needed */ + if (clientinlen > 1024) { +#ifdef _SUN_SDK_ + sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR, + "CRAM-MD5 input longer than 1024 bytes"); +#else + SETERROR(sparams->utils, "CRAM-MD5 input longer than 1024 bytes"); +#endif /* _SUN_SDK_ */ + return SASL_BADPROT; + } + + switch (text->state) { + + case 1: + return crammd5_server_mech_step1(text, sparams, + clientin, clientinlen, + serverout, serveroutlen, + oparams); + + case 2: + return crammd5_server_mech_step2(text, sparams, + clientin, clientinlen, + serverout, serveroutlen, + oparams); + + default: /* should never get here */ +#ifdef _SUN_SDK_ + sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR, + "Invalid CRAM-MD5 server step %d", text->state); +#else + sparams->utils->log(NULL, SASL_LOG_ERR, + "Invalid CRAM-MD5 server step %d\n", text->state); +#endif /* _SUN_SDK_ */ + return SASL_FAIL; + } + +#ifndef _SUN_SDK_ + return SASL_FAIL; /* should never get here */ +#endif /* !_SUN_SDK_ */ +} + +static void crammd5_server_mech_dispose(void *conn_context, + const sasl_utils_t *utils) +{ + server_context_t *text = (server_context_t *) conn_context; + + if (!text) return; + + if (text->challenge) _plug_free_string(utils,&(text->challenge)); + + utils->free(text); +} + +static sasl_server_plug_t crammd5_server_plugins[] = +{ + { + "CRAM-MD5", /* mech_name */ + 0, /* max_ssf */ + SASL_SEC_NOPLAINTEXT + | SASL_SEC_NOANONYMOUS, /* security_flags */ + SASL_FEAT_SERVER_FIRST, /* features */ + NULL, /* glob_context */ + &crammd5_server_mech_new, /* mech_new */ + &crammd5_server_mech_step, /* mech_step */ + &crammd5_server_mech_dispose, /* mech_dispose */ + NULL, /* mech_free */ + NULL, /* setpass */ + NULL, /* user_query */ + NULL, /* idle */ + NULL, /* mech avail */ + NULL /* spare */ + } +}; + +int crammd5_server_plug_init(const sasl_utils_t *utils, + int maxversion, + int *out_version, + sasl_server_plug_t **pluglist, + int *plugcount) +{ + if (maxversion < SASL_SERVER_PLUG_VERSION) { +#ifdef _SUN_SDK_ + utils->log(NULL, SASL_LOG_ERR, "CRAM version mismatch"); +#else + SETERROR( utils, "CRAM version mismatch"); +#endif /* _SUN_SDK_ */ + return SASL_BADVERS; + } + + *out_version = SASL_SERVER_PLUG_VERSION; + *pluglist = crammd5_server_plugins; + *plugcount = 1; + + return SASL_OK; +} + +/***************************** Client Section *****************************/ + +typedef struct client_context { + char *out_buf; + unsigned out_buf_len; +#ifdef _INTEGRATED_SOLARIS_ + void *h; +#endif /* _INTEGRATED_SOLARIS_ */ +} client_context_t; + +static int crammd5_client_mech_new(void *glob_context __attribute__((unused)), + sasl_client_params_t *params, + void **conn_context) +{ + client_context_t *text; + + /* holds state are in */ + text = params->utils->malloc(sizeof(client_context_t)); + if (text == NULL) { + MEMERROR(params->utils); + return SASL_NOMEM; + } + + memset(text, 0, sizeof(client_context_t)); + + *conn_context = text; + + return SASL_OK; +} + +static char *make_hashed(sasl_secret_t *sec, char *nonce, int noncelen, + const sasl_utils_t *utils) +{ + char secret[65]; + unsigned char digest[24]; + int lup; + char *in16; + + if (sec == NULL) return NULL; + + if (sec->len < 64) { + memcpy(secret, sec->data, sec->len); + + /* fill in rest with 0's */ + for (lup= sec->len; lup < 64; lup++) + secret[lup]='\0'; + + } else { + memcpy(secret, sec->data, 64); + } + + /* do the hmac md5 hash output 128 bits */ + utils->hmac_md5((unsigned char *) nonce, noncelen, + (unsigned char *) secret, 64, digest); + + /* convert that to hex form */ + in16 = convert16(digest, 16, utils); + if (in16 == NULL) return NULL; + + return in16; +} + +static int crammd5_client_mech_step(void *conn_context, + sasl_client_params_t *params, + const char *serverin, + unsigned serverinlen, + sasl_interact_t **prompt_need, + const char **clientout, + unsigned *clientoutlen, + sasl_out_params_t *oparams) +{ + client_context_t *text = (client_context_t *) conn_context; + const char *authid; + sasl_secret_t *password = NULL; + unsigned int free_password = 0; /* set if we need to free password */ + int auth_result = SASL_OK; + int pass_result = SASL_OK; + int result; + int maxsize; + char *in16 = NULL; + + *clientout = NULL; + *clientoutlen = 0; + + /* First check for absurd lengths */ + if (serverinlen > 1024) { +#ifdef _SUN_SDK_ + params->utils->log(params->utils->conn, SASL_LOG_ERR, + "CRAM-MD5 input longer than 1024 bytes"); +#else + params->utils->seterror(params->utils->conn, 0, + "CRAM-MD5 input longer than 1024 bytes"); +#endif /* _SUN_SDK_ */ + return SASL_BADPROT; + } + + /* check if sec layer strong enough */ + if (params->props.min_ssf > params->external_ssf) { +#ifdef _SUN_SDK_ + params->utils->log(params->utils->conn, SASL_LOG_ERR, + "SSF requested of CRAM-MD5 plugin"); +#else + SETERROR( params->utils, "SSF requested of CRAM-MD5 plugin"); +#endif /* _SUN_SDK_ */ + return SASL_TOOWEAK; + } + + /* try to get the userid */ + if (oparams->authid == NULL) { + auth_result=_plug_get_authid(params->utils, &authid, prompt_need); + + if ((auth_result != SASL_OK) && (auth_result != SASL_INTERACT)) + return auth_result; + } + + /* try to get the password */ + if (password == NULL) { + pass_result=_plug_get_password(params->utils, &password, + &free_password, prompt_need); + + if ((pass_result != SASL_OK) && (pass_result != SASL_INTERACT)) + return pass_result; + } + + /* free prompts we got */ + if (prompt_need && *prompt_need) { + params->utils->free(*prompt_need); + *prompt_need = NULL; + } + + /* if there are prompts not filled in */ + if ((auth_result == SASL_INTERACT) || (pass_result == SASL_INTERACT)) { + /* make the prompt list */ + result = +#ifdef _INTEGRATED_SOLARIS_ + _plug_make_prompts(params->utils, &text->h, prompt_need, + NULL, NULL, + auth_result == SASL_INTERACT ? + convert_prompt(params->utils, &text->h, + gettext("Please enter your authentication name")) + : NULL, NULL, + pass_result == SASL_INTERACT ? + convert_prompt(params->utils, &text->h, + gettext("Please enter your password")) + : NULL, NULL, + NULL, NULL, NULL, + NULL, NULL, NULL); +#else + _plug_make_prompts(params->utils, prompt_need, + NULL, NULL, + auth_result == SASL_INTERACT ? + "Please enter your authentication name" : NULL, + NULL, + pass_result == SASL_INTERACT ? + "Please enter your password" : NULL, NULL, + NULL, NULL, NULL, + NULL, NULL, NULL); +#endif /* _INTEGRATED_SOLARIS_ */ + if (result != SASL_OK) goto cleanup; + + return SASL_INTERACT; + } + + if (!password) { + PARAMERROR(params->utils); + return SASL_BADPARAM; + } + + result = params->canon_user(params->utils->conn, authid, 0, + SASL_CU_AUTHID | SASL_CU_AUTHZID, oparams); + if (result != SASL_OK) goto cleanup; + + /* + * username SP digest (keyed md5 where key is passwd) + */ + + in16 = make_hashed(password, (char *) serverin, serverinlen, + params->utils); + + if (in16 == NULL) { +#ifdef _SUN_SDK_ + params->utils->log(params->utils->conn, SASL_LOG_ERR, + "make_hashed failed"); +#else + SETERROR(params->utils, "whoops, make_hashed failed us this time"); +#endif /* _SUN_SDK_ */ + result = SASL_FAIL; + goto cleanup; + } + + maxsize = 32+1+strlen(oparams->authid)+30; + result = _plug_buf_alloc(params->utils, &(text->out_buf), + &(text->out_buf_len), maxsize); + if (result != SASL_OK) goto cleanup; + + snprintf(text->out_buf, maxsize, "%s %s", oparams->authid, in16); + + *clientout = text->out_buf; + *clientoutlen = strlen(*clientout); + + /* set oparams */ + oparams->doneflag = 1; + oparams->mech_ssf = 0; + oparams->maxoutbuf = 0; + oparams->encode_context = NULL; + oparams->encode = NULL; + oparams->decode_context = NULL; + oparams->decode = NULL; + oparams->param_version = 0; + + result = SASL_OK; + + cleanup: + /* get rid of private information */ + if (in16) _plug_free_string(params->utils, &in16); + + /* get rid of all sensitive info */ + if (free_password) _plug_free_secret(params-> utils, &password); + + return result; +} + +static void crammd5_client_mech_dispose(void *conn_context, + const sasl_utils_t *utils) +{ + client_context_t *text = (client_context_t *) conn_context; + + if (!text) return; + +#ifdef _INTEGRATED_SOLARIS_ + convert_prompt(utils, &text->h, NULL); +#endif /* _INTEGRATED_SOLARIS_ */ + if (text->out_buf) utils->free(text->out_buf); + + utils->free(text); +} + +static sasl_client_plug_t crammd5_client_plugins[] = +{ + { + "CRAM-MD5", /* mech_name */ + 0, /* max_ssf */ + SASL_SEC_NOPLAINTEXT + | SASL_SEC_NOANONYMOUS, /* security_flags */ + SASL_FEAT_SERVER_FIRST, /* features */ + NULL, /* required_prompts */ + NULL, /* glob_context */ + &crammd5_client_mech_new, /* mech_new */ + &crammd5_client_mech_step, /* mech_step */ + &crammd5_client_mech_dispose, /* mech_dispose */ + NULL, /* mech_free */ + NULL, /* idle */ + NULL, /* spare */ + NULL /* spare */ + } +}; + +int crammd5_client_plug_init(const sasl_utils_t *utils, + int maxversion, + int *out_version, + sasl_client_plug_t **pluglist, + int *plugcount) +{ + if (maxversion < SASL_CLIENT_PLUG_VERSION) { +#ifdef _SUN_SDK_ + utils->log(NULL, SASL_LOG_ERR, "CRAM version mismatch"); +#else + SETERROR( utils, "CRAM version mismatch"); +#endif /* _SUN_SDK_ */ + return SASL_BADVERS; + } + + *out_version = SASL_CLIENT_PLUG_VERSION; + *pluglist = crammd5_client_plugins; + *plugcount = 1; + + return SASL_OK; +} diff --git a/usr/src/lib/sasl_plugins/cram/crammd5_init.c b/usr/src/lib/sasl_plugins/cram/crammd5_init.c new file mode 100644 index 0000000000..5bfe6ebb62 --- /dev/null +++ b/usr/src/lib/sasl_plugins/cram/crammd5_init.c @@ -0,0 +1,44 @@ +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <config.h> + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#ifndef macintosh +#include <sys/stat.h> +#endif +#include <fcntl.h> +#include <assert.h> + +#include <sasl.h> +#include <saslplug.h> +#include <saslutil.h> + +#include "plugin_common.h" + +#ifdef macintosh +#include <sasl_crammd5_plugin_decl.h> +#endif + +#ifdef WIN32 +BOOL APIENTRY DllMain( HANDLE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved + ) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} +#endif + +SASL_CLIENT_PLUG_INIT( crammd5 ) +SASL_SERVER_PLUG_INIT( crammd5 ) + diff --git a/usr/src/lib/sasl_plugins/cram/i386/Makefile b/usr/src/lib/sasl_plugins/cram/i386/Makefile new file mode 100644 index 0000000000..952cdf4ddd --- /dev/null +++ b/usr/src/lib/sasl_plugins/cram/i386/Makefile @@ -0,0 +1,11 @@ +# +# Copyright 2003 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +# lib/libsasl/plugins/cram/i386/Makefile + +include ../Makefile.com + +install: all $(ROOTLIBS) diff --git a/usr/src/lib/sasl_plugins/cram/sparc/Makefile b/usr/src/lib/sasl_plugins/cram/sparc/Makefile new file mode 100644 index 0000000000..aedaedd885 --- /dev/null +++ b/usr/src/lib/sasl_plugins/cram/sparc/Makefile @@ -0,0 +1,11 @@ +# +# Copyright 2003 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +# lib/libsasl/plugins/cram/sparc/Makefile + +include ../Makefile.com + +install: all $(ROOTLIBS) diff --git a/usr/src/lib/sasl_plugins/cram/sparcv9/Makefile b/usr/src/lib/sasl_plugins/cram/sparcv9/Makefile new file mode 100644 index 0000000000..342bf6b8f1 --- /dev/null +++ b/usr/src/lib/sasl_plugins/cram/sparcv9/Makefile @@ -0,0 +1,12 @@ +# +# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +# lib/libsasl/plugins/cram/sparcv9/Makefile + +include ../Makefile.com +include ../../../Makefile.lib.64 + +install: all $(ROOTLIBS64) diff --git a/usr/src/lib/sasl_plugins/cram/spec/Makefile b/usr/src/lib/sasl_plugins/cram/spec/Makefile new file mode 100644 index 0000000000..9e331dcd31 --- /dev/null +++ b/usr/src/lib/sasl_plugins/cram/spec/Makefile @@ -0,0 +1,10 @@ +# +# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +# lib/libsasl/plugins/cram/spec/Makefile +# + +include $(SRC)/lib/Makefile.spec.arch diff --git a/usr/src/lib/sasl_plugins/cram/spec/Makefile.targ b/usr/src/lib/sasl_plugins/cram/spec/Makefile.targ new file mode 100644 index 0000000000..66db220e45 --- /dev/null +++ b/usr/src/lib/sasl_plugins/cram/spec/Makefile.targ @@ -0,0 +1,11 @@ +# +# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +LIBRARY = crammd5.a +VERS = .1 + +OBJECTS = cram.o diff --git a/usr/src/lib/sasl_plugins/cram/spec/amd64/Makefile b/usr/src/lib/sasl_plugins/cram/spec/amd64/Makefile new file mode 100644 index 0000000000..07e64367c4 --- /dev/null +++ b/usr/src/lib/sasl_plugins/cram/spec/amd64/Makefile @@ -0,0 +1,14 @@ +# +# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +.KEEP_STATE: + +include ../Makefile.targ + +include $(SRC)/lib/Makefile.lib +include $(SRC)/lib/Makefile.lib.64 +include $(SRC)/lib/Makefile.spec diff --git a/usr/src/lib/sasl_plugins/cram/spec/cram.spec b/usr/src/lib/sasl_plugins/cram/spec/cram.spec new file mode 100644 index 0000000000..37538e788e --- /dev/null +++ b/usr/src/lib/sasl_plugins/cram/spec/cram.spec @@ -0,0 +1,28 @@ +# +# Copyright 2003 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +# lib/libsasl/plugin/cram/spec/cram.spec +# + +function sasl_client_plug_init +include <sasl/saslplug.h> +declaration int sasl_client_plug_init(const sasl_utils_t *utils, \ + int maxversion, int *out_version, \ + sasl_client_plug_t **pluglist, \ + int *plugcount) +exception $return == SASL_FAIL +version SUNWprivate_1.1 +end + +function sasl_server_plug_init +include <sasl/saslplug.h> +declaration int sasl_server_plug_init(const sasl_utils_t *utils, \ + int maxversion, int *out_version, \ + sasl_server_plug_t **pluglist, \ + int *plugcount) +exception $return == SASL_FAIL +version SUNWprivate_1.1 +end diff --git a/usr/src/lib/sasl_plugins/cram/spec/i386/Makefile b/usr/src/lib/sasl_plugins/cram/spec/i386/Makefile new file mode 100644 index 0000000000..6306093f99 --- /dev/null +++ b/usr/src/lib/sasl_plugins/cram/spec/i386/Makefile @@ -0,0 +1,15 @@ +# +# Copyright 2003 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# lib/libsasl/plugins/cram/spec/i386/Makefile +# + +.KEEP_STATE: + +include ../Makefile.targ + +include $(SRC)/lib/Makefile.lib +include $(SRC)/lib/Makefile.spec diff --git a/usr/src/lib/sasl_plugins/cram/spec/sparc/Makefile b/usr/src/lib/sasl_plugins/cram/spec/sparc/Makefile new file mode 100644 index 0000000000..dcbd1f7c3c --- /dev/null +++ b/usr/src/lib/sasl_plugins/cram/spec/sparc/Makefile @@ -0,0 +1,15 @@ +# +# Copyright 2003 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# lib/libsasl/plugins/cram/spec/sparc/Makefile +# + +.KEEP_STATE: + +include ../Makefile.targ + +include $(SRC)/lib/Makefile.lib +include $(SRC)/lib/Makefile.spec diff --git a/usr/src/lib/sasl_plugins/cram/spec/sparcv9/Makefile b/usr/src/lib/sasl_plugins/cram/spec/sparcv9/Makefile new file mode 100644 index 0000000000..0c01722717 --- /dev/null +++ b/usr/src/lib/sasl_plugins/cram/spec/sparcv9/Makefile @@ -0,0 +1,16 @@ +# +# Copyright 2003 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# lib/libsasl/plugins/cram/spec/sparcv9/Makefile +# + +.KEEP_STATE: + +include ../Makefile.targ + +include $(SRC)/lib/Makefile.lib +include $(SRC)/lib/Makefile.lib.64 +include $(SRC)/lib/Makefile.spec diff --git a/usr/src/lib/sasl_plugins/cram/spec/versions b/usr/src/lib/sasl_plugins/cram/spec/versions new file mode 100644 index 0000000000..6a318d48a3 --- /dev/null +++ b/usr/src/lib/sasl_plugins/cram/spec/versions @@ -0,0 +1,19 @@ +# +# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +sparc { + SUNWprivate_1.1; +} +sparcv9 { + SUNWprivate_1.1; +} +i386 { + SUNWprivate_1.1; +} +amd64 { + SUNWprivate_1.1; +} diff --git a/usr/src/lib/sasl_plugins/digestmd5/Makefile b/usr/src/lib/sasl_plugins/digestmd5/Makefile new file mode 100644 index 0000000000..4897cf88f3 --- /dev/null +++ b/usr/src/lib/sasl_plugins/digestmd5/Makefile @@ -0,0 +1,7 @@ +# +# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" + +include ../Makefile.subdirs diff --git a/usr/src/lib/sasl_plugins/digestmd5/Makefile.com b/usr/src/lib/sasl_plugins/digestmd5/Makefile.com new file mode 100644 index 0000000000..4bc19b7933 --- /dev/null +++ b/usr/src/lib/sasl_plugins/digestmd5/Makefile.com @@ -0,0 +1,16 @@ +# +# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" + +LIBRARY= digestmd5.a +VERS= .1 + +PLUG_OBJS= digestmd5.o digestmd5_init.o + +PLUG_LIBS= -lpkcs11 +ENC_FLAGS= -DUSE_UEF_SERVER=1 -DUSE_UEF_CLIENT=1 -DUSE_UEF=1 + +# include common definitions +include ../../Makefile.com diff --git a/usr/src/lib/sasl_plugins/digestmd5/amd64/Makefile b/usr/src/lib/sasl_plugins/digestmd5/amd64/Makefile new file mode 100644 index 0000000000..13ae0db12d --- /dev/null +++ b/usr/src/lib/sasl_plugins/digestmd5/amd64/Makefile @@ -0,0 +1,11 @@ +# +# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +include ../Makefile.com +include ../../../Makefile.lib.64 + +install: all $(ROOTLIBS64) diff --git a/usr/src/lib/sasl_plugins/digestmd5/digestmd5.c b/usr/src/lib/sasl_plugins/digestmd5/digestmd5.c new file mode 100644 index 0000000000..3b5733d3af --- /dev/null +++ b/usr/src/lib/sasl_plugins/digestmd5/digestmd5.c @@ -0,0 +1,5433 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* 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 + * the documentation and/or other materials provided with the + * 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 +#include <sys/types.h> +#include <sys/stat.h> +#endif +#include <fcntl.h> +#include <ctype.h> + +/* EXPORT DELETE START */ +/* DES support */ +#ifdef WITH_DES +# ifdef WITH_SSL_DES +# include <openssl/des.h> +# else /* system DES library */ +# include <des.h> +# endif +#endif /* WITH_DES */ +/* EXPORT DELETE END */ + +#ifdef WIN32 +# include <winsock.h> +#else /* Unix */ +# include <netinet/in.h> +#endif /* WIN32 */ + +#ifdef _SUN_SDK_ +#include <unistd.h> +#endif /* _SUN_SDK_ */ + +#include <sasl.h> +#include <saslplug.h> + +#include "plugin_common.h" + +#if defined _SUN_SDK_ && defined USE_UEF +#include <security/cryptoki.h> +static int uef_init(const sasl_utils_t *utils); +#endif /* _SUN_SDK_ && USE_UEF */ + +#ifndef WIN32 +extern int strcasecmp(const char *s1, const char *s2); +#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 TRUE (1) +#define FALSE (0) +#endif + +#define DEFAULT_BUFSIZE 0xFFFF + +/***************************** Common Section *****************************/ + +#ifndef _SUN_SDK_ +static const char plugin_id[] = "$Id: digestmd5.c,v 1.153 2003/03/30 22:17:06 leg Exp $"; +#endif /* !_SUN_SDK_ */ + +/* Definitions */ +#define NONCE_SIZE (32) /* arbitrary */ + +/* Layer Flags */ +#define DIGEST_NOLAYER (1) +#define DIGEST_INTEGRITY (2) +#define DIGEST_PRIVACY (4) + +/* defines */ +#define HASHLEN 16 +typedef unsigned char HASH[HASHLEN + 1]; +#define HASHHEXLEN 32 +typedef unsigned char HASHHEX[HASHHEXLEN + 1]; + +#define MAC_SIZE 10 +#define MAC_OFFS 2 + +const char *SEALING_CLIENT_SERVER="Digest H(A1) to client-to-server sealing key magic constant"; +const char *SEALING_SERVER_CLIENT="Digest H(A1) to server-to-client sealing key magic constant"; + +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"; + +#define HT (9) +#define CR (13) +#define LF (10) +#define SP (32) +#define DEL (127) + +struct context; + +/* function definitions for cipher encode/decode */ +typedef int cipher_function_t(struct context *, + const char *, + unsigned, + unsigned char[], + char *, + unsigned *); + +#ifdef _SUN_SDK_ +typedef int cipher_init_t(struct context *, char [16], + char [16]); +#else +typedef int cipher_init_t(struct context *, unsigned char [16], + unsigned char [16]); +#endif /* _SUN_SDK_ */ + +typedef void cipher_free_t(struct context *); + +enum Context_type { SERVER = 0, CLIENT = 1 }; + +typedef struct cipher_context cipher_context_t; + +/* 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 { + time_t timestamp; + } s; /* server stuff */ + + struct { + char *serverFQDN; + int protection; + struct digest_cipher *cipher; + unsigned int server_maxbuf; + } c; /* client stuff */ + } u; +} reauth_entry_t; + +typedef struct reauth_cache { + /* static stuff */ + enum Context_type i_am; /* are we the client or server? */ + time_t timeout; + void *mutex; + size_t size; + + reauth_entry_t *e; /* fixed-size hash table of entries */ +} reauth_cache_t; + +/* context that stores info */ +typedef struct context { + int state; /* state in the authentication we are in */ + enum Context_type i_am; /* are we the client or server? */ + + reauth_cache_t *reauth; + + char *authid; + char *realm; + unsigned char *nonce; + unsigned int nonce_count; + unsigned char *cnonce; + + char *response_value; + + unsigned int seqnum; + unsigned int rec_seqnum; /* for checking integrity */ + + HASH Ki_send; + HASH Ki_receive; + + HASH HA1; /* Kcc or Kcs */ + + /* copy of utils from the params structures */ + const sasl_utils_t *utils; + + /* For general use */ + char *out_buf; + unsigned out_buf_len; + + /* for encoding/decoding */ + buffer_info_t *enc_in_buf; + char *encode_buf, *decode_buf, *decode_once_buf; + unsigned encode_buf_len, decode_buf_len, decode_once_buf_len; + char *decode_tmp_buf; + unsigned decode_tmp_buf_len; + char *MAC_buf; + unsigned MAC_buf_len; + + char *buffer; + char sizebuf[4]; + int cursize; + + /* Layer info */ + unsigned int size; /* Absolute size of buffer */ + unsigned int needsize; /* How much of the size of the buffer is left */ + + /* 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 */ + cipher_function_t *cipher_enc; + cipher_function_t *cipher_dec; + cipher_init_t *cipher_init; + cipher_free_t *cipher_free; + struct cipher_context *cipher_enc_context; + struct cipher_context *cipher_dec_context; +} context_t; + +struct digest_cipher { + char *name; + sasl_ssf_t ssf; + int n; /* bits to make privacy key */ + int flag; /* a bitmask to make things easier for us */ + + cipher_function_t *cipher_enc; + cipher_function_t *cipher_dec; + cipher_init_t *cipher_init; + cipher_free_t *cipher_free; +}; + +#ifdef _SUN_SDK_ +static const unsigned char *COLON = (unsigned char *)":"; +#else +static const unsigned char *COLON = ":"; +#endif /* _SUN_SDK_ */ + +/* Hashes a string to produce an unsigned short */ +static unsigned hash(const char *str) +{ + unsigned val = 0; + int i; + + while (str && *str) { + i = (int) *str; + val ^= i; + val <<= 1; + str++; + } + + return val; +} + +static void CvtHex(HASH Bin, HASHHEX Hex) +{ + unsigned short i; + unsigned char j; + + for (i = 0; i < HASHLEN; i++) { + j = (Bin[i] >> 4) & 0xf; + if (j <= 9) + Hex[i * 2] = (j + '0'); + else + Hex[i * 2] = (j + 'a' - 10); + j = Bin[i] & 0xf; + if (j <= 9) + Hex[i * 2 + 1] = (j + '0'); + else + Hex[i * 2 + 1] = (j + 'a' - 10); + } + Hex[HASHHEXLEN] = '\0'; +} + +/* + * calculate request-digest/response-digest as per HTTP Digest spec + */ +void +DigestCalcResponse(const sasl_utils_t * utils, + HASHHEX HA1, /* H(A1) */ + 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, + HASHHEX HEntity, /* H(entity body) if qop="auth-int" */ + HASHHEX Response /* request-digest or response-digest */ + ) +{ + MD5_CTX Md5Ctx; + HASH HA2; + HASH RespHash; + HASHHEX HA2Hex; + char ncvalue[10]; + + /* calculate H(A2) */ + utils->MD5Init(&Md5Ctx); + + if (pszMethod != NULL) { + utils->MD5Update(&Md5Ctx, pszMethod, strlen((char *) pszMethod)); + } + utils->MD5Update(&Md5Ctx, (unsigned char *) COLON, 1); + + /* utils->MD5Update(&Md5Ctx, (unsigned char *) "AUTHENTICATE:", 13); */ + utils->MD5Update(&Md5Ctx, pszDigestUri, strlen((char *) pszDigestUri)); + if (strcasecmp((char *) pszQop, "auth") != 0) { + /* append ":00000000000000000000000000000000" */ + utils->MD5Update(&Md5Ctx, COLON, 1); + utils->MD5Update(&Md5Ctx, HEntity, HASHHEXLEN); + } + utils->MD5Final(HA2, &Md5Ctx); + CvtHex(HA2, HA2Hex); + + /* calculate response */ + utils->MD5Init(&Md5Ctx); + utils->MD5Update(&Md5Ctx, HA1, HASHHEXLEN); + utils->MD5Update(&Md5Ctx, COLON, 1); + utils->MD5Update(&Md5Ctx, pszNonce, strlen((char *) pszNonce)); + utils->MD5Update(&Md5Ctx, COLON, 1); + if (*pszQop) { + sprintf(ncvalue, "%08x", pszNonceCount); +#ifdef _SUN_SDK_ + utils->MD5Update(&Md5Ctx, (unsigned char *)ncvalue, strlen(ncvalue)); +#else + utils->MD5Update(&Md5Ctx, ncvalue, strlen(ncvalue)); +#endif /* _SUN_SDK_ */ + utils->MD5Update(&Md5Ctx, COLON, 1); + utils->MD5Update(&Md5Ctx, pszCNonce, strlen((char *) pszCNonce)); + utils->MD5Update(&Md5Ctx, COLON, 1); + utils->MD5Update(&Md5Ctx, pszQop, strlen((char *) pszQop)); + utils->MD5Update(&Md5Ctx, COLON, 1); + } + utils->MD5Update(&Md5Ctx, HA2Hex, HASHHEXLEN); + utils->MD5Final(RespHash, &Md5Ctx); + CvtHex(RespHash, Response); +} + +static bool UTF8_In_8859_1(const unsigned char *base, int len) +{ + const unsigned char *scan, *end; + + end = base + len; + for (scan = base; scan < end; ++scan) { + if (*scan > 0xC3) + break; /* abort if outside 8859-1 */ + if (*scan >= 0xC0 && *scan <= 0xC3) { + if (++scan == end || *scan < 0x80 || *scan > 0xBF) + break; + } + } + + /* if scan >= end, then this is a 8859-1 string. */ + return (scan >= end); +} + +/* + * if the string is entirely in the 8859-1 subset of UTF-8, then translate to + * 8859-1 prior to MD5 + */ +void MD5_UTF8_8859_1(const sasl_utils_t * utils, + MD5_CTX * ctx, + bool In_ISO_8859_1, + const unsigned char *base, + int len) +{ + const unsigned char *scan, *end; + unsigned char cbuf; + + end = base + len; + + /* if we found a character outside 8859-1, don't alter string */ + if (!In_ISO_8859_1) { + utils->MD5Update(ctx, base, len); + return; + } + /* convert to 8859-1 prior to applying hash */ + do { + for (scan = base; scan < end && *scan < 0xC0; ++scan); + if (scan != base) + utils->MD5Update(ctx, base, scan - base); + if (scan + 1 >= end) + break; + cbuf = ((scan[0] & 0x3) << 6) | (scan[1] & 0x3f); + utils->MD5Update(ctx, &cbuf, 1); + base = scan + 2; + } + while (base < end); +} + +static void DigestCalcSecret(const sasl_utils_t * utils, + unsigned char *pszUserName, + unsigned char *pszRealm, + unsigned char *Password, + int PasswordLen, + HASH HA1) +{ + bool In_8859_1; + + MD5_CTX Md5Ctx; + + /* 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 shoud use code example instead */ + + utils->MD5Init(&Md5Ctx); + + /* We have to convert UTF-8 to ISO-8859-1 if possible */ + In_8859_1 = UTF8_In_8859_1(pszUserName, strlen((char *) pszUserName)); + MD5_UTF8_8859_1(utils, &Md5Ctx, In_8859_1, + pszUserName, strlen((char *) pszUserName)); + + utils->MD5Update(&Md5Ctx, COLON, 1); + + if (pszRealm != NULL && pszRealm[0] != '\0') { + /* a NULL realm is equivalent to the empty string */ + utils->MD5Update(&Md5Ctx, pszRealm, strlen((char *) pszRealm)); + } + + utils->MD5Update(&Md5Ctx, COLON, 1); + + /* We have to convert UTF-8 to ISO-8859-1 if possible */ + In_8859_1 = UTF8_In_8859_1(Password, PasswordLen); + MD5_UTF8_8859_1(utils, &Md5Ctx, In_8859_1, + Password, PasswordLen); + + utils->MD5Final(HA1, &Md5Ctx); +} + +static unsigned char *create_nonce(const sasl_utils_t * utils) +{ + unsigned char *base64buf; + int base64len; + + char *ret = (char *) utils->malloc(NONCE_SIZE); + if (ret == NULL) + return NULL; + +#if defined _DEV_URANDOM && defined _SUN_SDK_ + { + int fd = open(_DEV_URANDOM, O_RDONLY); + int nread = 0; + + if (fd != -1) { + nread = read(fd, ret, NONCE_SIZE); + close(fd); + } + if (nread != NONCE_SIZE) + utils->rand(utils->rpool, (char *) ret, NONCE_SIZE); + } +#else + utils->rand(utils->rpool, (char *) ret, NONCE_SIZE); +#endif /* _DEV_URANDOM && _SUN_SDK_ */ + + /* base 64 encode it so it has valid chars */ + base64len = (NONCE_SIZE * 4 / 3) + (NONCE_SIZE % 3 ? 4 : 0); + + base64buf = (unsigned char *) utils->malloc(base64len + 1); + if (base64buf == NULL) { +#ifdef _SUN_SDK_ + utils->log(utils->conn, SASL_LOG_ERR, + "Unable to allocate final buffer"); +#else + utils->seterror(utils->conn, 0, "Unable to allocate final buffer"); +#endif /* _SUN_SDK_ */ + return NULL; + } + + /* + * Returns SASL_OK on success, SASL_BUFOVER if result won't fit + */ + if (utils->encode64(ret, NONCE_SIZE, + (char *) base64buf, base64len, NULL) != SASL_OK) { + utils->free(ret); + return NULL; + } + utils->free(ret); + + return base64buf; +} + +static int add_to_challenge(const sasl_utils_t *utils, + char **str, unsigned *buflen, unsigned *curlen, + char *name, + unsigned char *value, + bool need_quotes) +{ + int namesize = strlen(name); + int valuesize = strlen((char *) value); + int ret; + + ret = _plug_buf_alloc(utils, str, buflen, + *curlen + 1 + namesize + 2 + valuesize + 2); + if(ret != SASL_OK) return ret; + + *curlen = *curlen + 1 + namesize + 2 + valuesize + 2; + + strcat(*str, ","); + strcat(*str, name); + + if (need_quotes) { + strcat(*str, "=\""); + strcat(*str, (char *) value); /* XXX. What about quoting??? */ + strcat(*str, "\""); + } else { + strcat(*str, "="); + strcat(*str, (char *) value); + } + + return SASL_OK; +} + +static char *skip_lws (char *s) +{ + if(!s) return NULL; + + /* skipping spaces: */ + while (s[0] == ' ' || s[0] == HT || s[0] == CR || s[0] == LF) { + if (s[0]=='\0') break; + s++; + } + + return s; +} + +#ifdef __SUN_SDK_ +static char *skip_token (char *s, int caseinsensitive __attribute__((unused))) +#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_ */ + if (s[0]==DEL || 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]==']' || 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 */ +static char *unquote (char *qstr) +{ + char *endvalue; + int escaped = 0; + char *outptr; + + if(!qstr) return NULL; + + if (qstr[0] == '"') { + qstr++; + outptr = qstr; + + for (endvalue = qstr; endvalue[0] != '\0'; endvalue++, outptr++) { + if (escaped) { + outptr[0] = endvalue[0]; + escaped = 0; + } + else if (endvalue[0] == '\\') { + escaped = 1; + outptr--; /* Will be incremented at the end of the loop */ + } + else if (endvalue[0] == '"') { + break; + } + else { + outptr[0] = endvalue[0]; + } + } + + if (endvalue[0] != '"') { + return NULL; + } + + while (outptr <= endvalue) { + outptr[0] = '\0'; + outptr++; + } + endvalue++; + } + else { /* not qouted value (token) */ + endvalue = skip_token(qstr,0); + }; + + return endvalue; +} + +static void get_pair(char **in, char **name, char **value) +{ + char *endpair; + /* int inQuotes; */ + char *curp = *in; + *name = NULL; + *value = NULL; + + if (curp == NULL) return; + if (curp[0] == '\0') return; + + /* skipping spaces: */ + curp = skip_lws(curp); + + *name = curp; + + curp = skip_token(curp,1); + + /* strip wierd chars */ + if (curp[0] != '=' && curp[0] != '\0') { + *curp++ = '\0'; + }; + + curp = skip_lws(curp); + + if (curp[0] != '=') { /* No '=' sign */ + *name = NULL; + return; + } + + curp[0] = '\0'; + curp++; + + curp = skip_lws(curp); + + *value = (curp[0] == '"') ? curp+1 : curp; + + endpair = unquote (curp); + if (endpair == NULL) { /* Unbalanced quotes */ + *name = NULL; + return; + } + if (endpair[0] != ',') { + if (endpair[0]!='\0') { + *endpair++ = '\0'; + } + } + + endpair = skip_lws(endpair); + + /* syntax check: MUST be '\0' or ',' */ + if (endpair[0] == ',') { + endpair[0] = '\0'; + endpair++; /* skipping <,> */ + } else if (endpair[0] != '\0') { + *name = NULL; + return; + } + + *in = endpair; +} + +/* EXPORT DELETE START */ +#ifdef WITH_DES +struct des_context_s { + des_key_schedule keysched; /* key schedule for des initialization */ + des_cblock ivec; /* initial vector for encoding */ + des_key_schedule keysched2; /* key schedule for 3des initialization */ +}; + +typedef struct des_context_s des_context_t; + +/* 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. */ +static void slidebits(unsigned char *keybuf, unsigned char *inbuf) +{ + keybuf[0] = inbuf[0]; + keybuf[1] = (inbuf[0]<<7) | (inbuf[1]>>1); + keybuf[2] = (inbuf[1]<<6) | (inbuf[2]>>2); + keybuf[3] = (inbuf[2]<<5) | (inbuf[3]>>3); + keybuf[4] = (inbuf[3]<<4) | (inbuf[4]>>4); + keybuf[5] = (inbuf[4]<<3) | (inbuf[5]>>5); + keybuf[6] = (inbuf[5]<<2) | (inbuf[6]>>6); + keybuf[7] = (inbuf[6]<<1); +} + +/****************************** + * + * 3DES functions + * + *****************************/ + +static int dec_3des(context_t *text, + const char *input, + unsigned inputlen, + unsigned char digest[16], + char *output, + unsigned *outputlen) +{ + des_context_t *c = (des_context_t *) text->cipher_dec_context; + int padding, p; + + des_ede2_cbc_encrypt((void *) input, + (void *) output, + inputlen, + c->keysched, + c->keysched2, + &c->ivec, + DES_DECRYPT); + + /* now chop off the padding */ + padding = output[inputlen - 11]; + if (padding < 1 || padding > 8) { + /* invalid padding length */ + return SASL_FAIL; + } + /* verify all padding is correct */ + for (p = 1; p <= padding; p++) { + if (output[inputlen - 10 - p] != padding) { + return SASL_FAIL; + } + } + + /* chop off the padding */ + *outputlen = inputlen - padding - 10; + + /* copy in the HMAC to digest */ + memcpy(digest, output + inputlen - 10, 10); + + return SASL_OK; +} + +static int enc_3des(context_t *text, + const char *input, + unsigned inputlen, + unsigned char digest[16], + char *output, + unsigned *outputlen) +{ + des_context_t *c = (des_context_t *) text->cipher_enc_context; + int len; + int paddinglen; + + /* determine padding length */ + paddinglen = 8 - ((inputlen + 10) % 8); + + /* now construct the full stuff to be ciphered */ + memcpy(output, input, inputlen); /* text */ + memset(output+inputlen, paddinglen, paddinglen);/* pad */ + memcpy(output+inputlen+paddinglen, digest, 10); /* hmac */ + + len=inputlen+paddinglen+10; + + des_ede2_cbc_encrypt((void *) output, + (void *) output, + len, + c->keysched, + c->keysched2, + &c->ivec, + DES_ENCRYPT); + + *outputlen=len; + + return SASL_OK; +} + +static int init_3des(context_t *text, + unsigned char enckey[16], + unsigned char deckey[16]) +{ + des_context_t *c; + unsigned char keybuf[8]; + + /* allocate enc & dec context */ + c = (des_context_t *) text->utils->malloc(2 * sizeof(des_context_t)); + if (c == NULL) return SASL_NOMEM; + + /* setup enc context */ + slidebits(keybuf, enckey); + if (des_key_sched((des_cblock *) keybuf, c->keysched) < 0) + return SASL_FAIL; + + slidebits(keybuf, enckey + 7); + if (des_key_sched((des_cblock *) keybuf, c->keysched2) < 0) + return SASL_FAIL; + memcpy(c->ivec, ((char *) enckey) + 8, 8); + + text->cipher_enc_context = (cipher_context_t *) c; + + /* setup dec context */ + c++; + slidebits(keybuf, deckey); + if (des_key_sched((des_cblock *) keybuf, c->keysched) < 0) + return SASL_FAIL; + + slidebits(keybuf, deckey + 7); + if (des_key_sched((des_cblock *) keybuf, c->keysched2) < 0) + return SASL_FAIL; + + memcpy(c->ivec, ((char *) deckey) + 8, 8); + + text->cipher_dec_context = (cipher_context_t *) c; + + return SASL_OK; +} + + +/****************************** + * + * DES functions + * + *****************************/ + +static int dec_des(context_t *text, + const char *input, + unsigned inputlen, + unsigned char digest[16], + char *output, + unsigned *outputlen) +{ + des_context_t *c = (des_context_t *) text->cipher_dec_context; + int p, padding = 0; + + des_cbc_encrypt((void *) input, + (void *) output, + inputlen, + c->keysched, + &c->ivec, + DES_DECRYPT); + + /* Update the ivec (des_cbc_encrypt implementations tend to be broken in + this way) */ + memcpy(c->ivec, input + (inputlen - 8), 8); + + /* now chop off the padding */ + padding = output[inputlen - 11]; + if (padding < 1 || padding > 8) { + /* invalid padding length */ + return SASL_FAIL; + } + /* verify all padding is correct */ + for (p = 1; p <= padding; p++) { + if (output[inputlen - 10 - p] != padding) { + return SASL_FAIL; + } + } + + /* chop off the padding */ + *outputlen = inputlen - padding - 10; + + /* copy in the HMAC to digest */ + memcpy(digest, output + inputlen - 10, 10); + + return SASL_OK; +} + +static int enc_des(context_t *text, + const char *input, + unsigned inputlen, + unsigned char digest[16], + char *output, + unsigned *outputlen) +{ + des_context_t *c = (des_context_t *) text->cipher_enc_context; + int len; + int paddinglen; + + /* determine padding length */ + paddinglen = 8 - ((inputlen+10) % 8); + + /* now construct the full stuff to be ciphered */ + memcpy(output, input, inputlen); /* text */ + memset(output+inputlen, paddinglen, paddinglen);/* pad */ + memcpy(output+inputlen+paddinglen, digest, 10); /* hmac */ + + len = inputlen + paddinglen + 10; + + des_cbc_encrypt((void *) output, + (void *) output, + len, + c->keysched, + &c->ivec, + DES_ENCRYPT); + + /* Update the ivec (des_cbc_encrypt implementations tend to be broken in + this way) */ + memcpy(c->ivec, output + (len - 8), 8); + + *outputlen = len; + + return SASL_OK; +} + +static int init_des(context_t *text, + unsigned char enckey[16], + unsigned char deckey[16]) +{ + des_context_t *c; + unsigned char keybuf[8]; + + /* allocate enc context */ + c = (des_context_t *) text->utils->malloc(2 * sizeof(des_context_t)); + if (c == NULL) return SASL_NOMEM; + + /* setup enc context */ + slidebits(keybuf, enckey); + des_key_sched((des_cblock *) keybuf, c->keysched); + + memcpy(c->ivec, ((char *) enckey) + 8, 8); + + text->cipher_enc_context = (cipher_context_t *) c; + + /* setup dec context */ + c++; + slidebits(keybuf, deckey); + des_key_sched((des_cblock *) keybuf, c->keysched); + + memcpy(c->ivec, ((char *) deckey) + 8, 8); + + text->cipher_dec_context = (cipher_context_t *) c; + + return SASL_OK; +} + +static void free_des(context_t *text) +{ + /* free des contextss. only cipher_enc_context needs to be free'd, + since cipher_dec_context was allocated at the same time. */ + if (text->cipher_enc_context) text->utils->free(text->cipher_enc_context); +} + +#endif /* WITH_DES */ + +#ifdef WITH_RC4 +/* quick generic implementation of RC4 */ +struct rc4_context_s { + unsigned char sbox[256]; + int i, j; +}; + +typedef struct rc4_context_s rc4_context_t; + +static void rc4_init(rc4_context_t *text, + const unsigned char *key, + unsigned keylen) +{ + int i, j; + + /* fill in linearly s0=0 s1=1... */ + for (i=0;i<256;i++) + text->sbox[i]=i; + + j=0; + for (i = 0; i < 256; i++) { + unsigned char tmp; + /* j = (j + Si + Ki) mod 256 */ + j = (j + text->sbox[i] + key[i % keylen]) % 256; + + /* swap Si and Sj */ + tmp = text->sbox[i]; + text->sbox[i] = text->sbox[j]; + text->sbox[j] = tmp; + } + + /* counters initialized to 0 */ + text->i = 0; + text->j = 0; +} + +static void rc4_encrypt(rc4_context_t *text, + const char *input, + char *output, + unsigned len) +{ + int tmp; + int i = text->i; + int j = text->j; + int t; + int K; + const char *input_end = input + len; + + while (input < input_end) { + i = (i + 1) % 256; + + j = (j + text->sbox[i]) % 256; + + /* swap Si and Sj */ + tmp = text->sbox[i]; + text->sbox[i] = text->sbox[j]; + text->sbox[j] = tmp; + + t = (text->sbox[i] + text->sbox[j]) % 256; + + K = text->sbox[t]; + + /* byte K is Xor'ed with plaintext */ + *output++ = *input++ ^ K; + } + + text->i = i; + text->j = j; +} + +static void rc4_decrypt(rc4_context_t *text, + const char *input, + char *output, + unsigned len) +{ + int tmp; + int i = text->i; + int j = text->j; + int t; + int K; + const char *input_end = input + len; + + while (input < input_end) { + i = (i + 1) % 256; + + j = (j + text->sbox[i]) % 256; + + /* swap Si and Sj */ + tmp = text->sbox[i]; + text->sbox[i] = text->sbox[j]; + text->sbox[j] = tmp; + + t = (text->sbox[i] + text->sbox[j]) % 256; + + K = text->sbox[t]; + + /* byte K is Xor'ed with plaintext */ + *output++ = *input++ ^ K; + } + + text->i = i; + text->j = j; +} + +static void free_rc4(context_t *text) +{ + /* free rc4 context structures */ + + if(text->cipher_enc_context) text->utils->free(text->cipher_enc_context); + if(text->cipher_dec_context) text->utils->free(text->cipher_dec_context); +#ifdef _SUN_SDK_ + text->cipher_enc_context = NULL; + text->cipher_dec_context = NULL; +#endif /* _SUN_SDK_ */ +} + +static int init_rc4(context_t *text, +#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 */ + text->cipher_enc_context= + (cipher_context_t *) text->utils->malloc(sizeof(rc4_context_t)); + if (text->cipher_enc_context == NULL) return SASL_NOMEM; + + text->cipher_dec_context= + (cipher_context_t *) text->utils->malloc(sizeof(rc4_context_t)); +#ifdef _SUN_SDK_ + if (text->cipher_dec_context == NULL) { + text->utils->free(text->cipher_enc_context); + text->cipher_enc_context = NULL; + return SASL_NOMEM; + } +#else + if (text->cipher_dec_context == NULL) return SASL_NOMEM; +#endif /* _SUN_SDK_ */ + + /* initialize them */ + rc4_init((rc4_context_t *) text->cipher_enc_context, + (const unsigned char *) enckey, 16); + rc4_init((rc4_context_t *) text->cipher_dec_context, + (const unsigned char *) deckey, 16); + + return SASL_OK; +} + +static int dec_rc4(context_t *text, + const char *input, + unsigned inputlen, + unsigned char digest[16], + char *output, + unsigned *outputlen) +{ + /* decrypt the text part */ + rc4_decrypt((rc4_context_t *) text->cipher_dec_context, + input, output, inputlen-10); + + /* decrypt the HMAC part */ + rc4_decrypt((rc4_context_t *) text->cipher_dec_context, + input+(inputlen-10), (char *) digest, 10); + + /* no padding so we just subtract the HMAC to get the text length */ + *outputlen = inputlen - 10; + + return SASL_OK; +} + +static int enc_rc4(context_t *text, + const char *input, + unsigned inputlen, + unsigned char digest[16], + char *output, + unsigned *outputlen) +{ + /* pad is zero */ + *outputlen = inputlen+10; + + /* encrypt the text part */ + rc4_encrypt((rc4_context_t *) text->cipher_enc_context, + input, + output, + inputlen); + + /* encrypt the HMAC part */ + rc4_encrypt((rc4_context_t *) text->cipher_enc_context, + (const char *) digest, + (output)+inputlen, 10); + + return SASL_OK; +} + +#endif /* WITH_RC4 */ +/* EXPORT DELETE END */ + +struct digest_cipher available_ciphers[] = +{ + /* EXPORT DELETE START */ +#ifdef WITH_RC4 + { "rc4-40", 40, 5, 0x01, &enc_rc4, &dec_rc4, &init_rc4, &free_rc4 }, + { "rc4-56", 56, 7, 0x02, &enc_rc4, &dec_rc4, &init_rc4, &free_rc4 }, + { "rc4", 128, 16, 0x04, &enc_rc4, &dec_rc4, &init_rc4, &free_rc4 }, +#endif +#ifdef WITH_DES + { "des", 55, 16, 0x08, &enc_des, &dec_des, &init_des, &free_des }, + { "3des", 112, 16, 0x10, &enc_3des, &dec_3des, &init_3des, &free_des }, +#endif + /* EXPORT DELETE END */ + { NULL, 0, 0, 0, NULL, NULL, NULL, NULL } +}; + + +#ifdef USE_UEF +DEFINE_STATIC_MUTEX(uef_init_mutex); +#define DES_CIPHER_INDEX 3 +#define DES3_CIPHER_INDEX 4 + +static int got_uef_slot = FALSE; +static sasl_ssf_t uef_max_ssf = 0; +static CK_SLOT_ID rc4_slot_id; +static CK_SLOT_ID des_slot_id; +static CK_SLOT_ID des3_slot_id; + +struct uef_context_s { + CK_SESSION_HANDLE hSession; + CK_OBJECT_HANDLE hKey; +}; + +typedef struct uef_context_s uef_context_t; + +/* + * 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. + */ + +static void slidebits(unsigned char *keybuf, unsigned char *inbuf) +{ + keybuf[0] = inbuf[0]; + keybuf[1] = (inbuf[0]<<7) | (inbuf[1]>>1); + keybuf[2] = (inbuf[1]<<6) | (inbuf[2]>>2); + keybuf[3] = (inbuf[2]<<5) | (inbuf[3]>>3); + keybuf[4] = (inbuf[3]<<4) | (inbuf[4]>>4); + keybuf[5] = (inbuf[4]<<3) | (inbuf[5]>>5); + keybuf[6] = (inbuf[5]<<2) | (inbuf[6]>>6); + keybuf[7] = (inbuf[6]<<1); +} + +/* + * 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 + */ + +static int init_uef(context_t *text, + CK_KEY_TYPE keyType, + CK_MECHANISM_TYPE mech_type, + CK_SLOT_ID slot_id, + char enckey[16], + char deckey[16]) +{ + CK_RV rv; + uef_context_t *enc_context; + uef_context_t *dec_context; + CK_OBJECT_CLASS class = CKO_SECRET_KEY; + CK_BBOOL true = TRUE; + static CK_MECHANISM mechanism = {CKM_RC4, NULL, 0}; + unsigned char keybuf[24]; + CK_ATTRIBUTE template[] = { + {CKA_CLASS, NULL, sizeof (class)}, + {CKA_KEY_TYPE, NULL, sizeof (keyType)}, + {CKA_ENCRYPT, NULL, sizeof (true)}, + {CKA_VALUE, NULL, 16}}; + + template[0].pValue = &class; + template[1].pValue = &keyType; + template[2].pValue = &true; + if (keyType == CKK_DES || keyType == CKK_DES3) { + slidebits(keybuf, (unsigned char *)enckey); + if (keyType == CKK_DES3) { + slidebits(keybuf + 8, (unsigned char *)enckey + 7); + (void) memcpy(keybuf + 16, keybuf, 8); + template[3].ulValueLen = 24; + } else { + template[3].ulValueLen = 8; + } + template[3].pValue = keybuf; + mechanism.pParameter = enckey + 8; + mechanism.ulParameterLen = 8; + } else { + template[3].pValue = enckey; + } + mechanism.mechanism = mech_type; + + /* allocate rc4 context structures */ + enc_context = text->utils->malloc(sizeof (uef_context_t)); + if (enc_context == NULL) + return SASL_NOMEM; + + rv = C_OpenSession(slot_id, CKF_SERIAL_SESSION, NULL_PTR, NULL_PTR, + &enc_context->hSession); + if (rv != CKR_OK) { + text->utils->free(enc_context); +#ifdef DEBUG + text->utils->log(text->utils->conn, SASL_LOG_DEBUG, + "enc C_OpenSession Failed:0x%.8X\n", rv); +#endif + return SASL_FAIL; + } + + rv = C_CreateObject(enc_context->hSession, template, + sizeof (template)/sizeof (template[0]), &enc_context->hKey); + if (rv != CKR_OK) { + text->utils->free(enc_context); + (void) C_CloseSession(enc_context->hSession); +#ifdef DEBUG + text->utils->log(text->utils->conn, SASL_LOG_DEBUG, + "enc C_CreateObject: rv = 0x%.8X\n", rv); +#endif + return SASL_FAIL; + } + + text->cipher_enc_context = (cipher_context_t *)enc_context; + + /* Initialize the encryption operation in the session */ + rv = C_EncryptInit(enc_context->hSession, &mechanism, enc_context->hKey); + if (rv != CKR_OK) { +#ifdef DEBUG + text->utils->log(text->utils->conn, SASL_LOG_DEBUG, + "C_EncryptInit: rv = 0x%.8X\n", rv); +#endif + return SASL_FAIL; + } + + dec_context = text->utils->malloc(sizeof(uef_context_t)); + if (dec_context == NULL) + return SASL_NOMEM; + + rv = C_OpenSession(slot_id, CKF_SERIAL_SESSION, NULL_PTR, NULL_PTR, + &dec_context->hSession); + if (rv != CKR_OK) { +#ifdef DEBUG + text->utils->log(text->utils->conn, SASL_LOG_DEBUG, + "dec C_OpenSession Failed:0x%.8X\n", rv); +#endif + text->utils->free(dec_context); + return SASL_FAIL; + } + + template[2].type = CKA_DECRYPT; + if (keyType == CKK_DES || keyType == CKK_DES3) { + slidebits(keybuf, (unsigned char *)deckey); + if (keyType == CKK_DES3) { + slidebits(keybuf + 8, (unsigned char *)deckey + 7); + (void) memcpy(keybuf + 16, keybuf, 8); + } + mechanism.pParameter = deckey + 8; + } else { + template[3].pValue = deckey; + } + + rv = C_CreateObject(dec_context->hSession, template, + sizeof (template)/sizeof (template[0]), &dec_context->hKey); + if (rv != CKR_OK) { +#ifdef DEBUG + text->utils->log(text->utils->conn, SASL_LOG_DEBUG, + "dec C_CreateObject: rv = 0x%.8X\n", rv); +#endif + (void) C_CloseSession(dec_context->hSession); + text->utils->free(dec_context); + return SASL_FAIL; + } + text->cipher_dec_context = (cipher_context_t *)dec_context; + + /* Initialize the decryption operation in the session */ + rv = C_DecryptInit(dec_context->hSession, &mechanism, dec_context->hKey); + if (rv != CKR_OK) { +#ifdef DEBUG + text->utils->log(text->utils->conn, SASL_LOG_DEBUG, + "C_DecryptInit: rv = 0x%.8X\n", rv); +#endif + return SASL_FAIL; + } + + return SASL_OK; +} + +static int init_rc4_uef(context_t *text, + char enckey[16], + char deckey[16]) +{ + return init_uef(text, CKK_RC4, CKM_RC4, rc4_slot_id, enckey, deckey); +} + +static int init_des_uef(context_t *text, + char enckey[16], + char deckey[16]) +{ + return init_uef(text, CKK_DES, CKM_DES_CBC, des_slot_id, enckey, deckey); +} + +static int init_3des_uef(context_t *text, + char enckey[16], + char deckey[16]) +{ + return init_uef(text, CKK_DES3, CKM_DES3_CBC, des3_slot_id, enckey, deckey); +} + +static void +free_uef(context_t *text) +{ + uef_context_t *enc_context = + (uef_context_t *)text->cipher_enc_context; + uef_context_t *dec_context = + (uef_context_t *)text->cipher_dec_context; + CK_RV rv; + unsigned char buf[1]; + CK_ULONG ulLen = 0; + + + if (enc_context != NULL) { + rv = C_EncryptFinal(enc_context->hSession, buf, &ulLen); + if (rv != CKR_OK) { +#ifdef DEBUG + text->utils->log(text->utils->conn, SASL_LOG_DEBUG, + "C_EncryptFinal failed:0x%.8X\n", rv); +#endif + } + rv = C_DestroyObject(enc_context->hSession, enc_context->hKey); + if (rv != CKR_OK) { +#ifdef DEBUG + text->utils->log(text->utils->conn, SASL_LOG_DEBUG, + "C_DestroyObject failed:0x%.8X\n", rv); +#endif + } + rv = C_CloseSession(enc_context->hSession); + if (rv != CKR_OK) { +#ifdef DEBUG + text->utils->log(text->utils->conn, SASL_LOG_DEBUG, + "C_CloseSession failed:0x%.8X\n", rv); +#endif + } + text->utils->free(enc_context); + } + if (dec_context != NULL) { + rv = C_DecryptFinal(dec_context->hSession, buf, &ulLen); + if (rv != CKR_OK) { +#ifdef DEBUG + text->utils->log(text->utils->conn, SASL_LOG_DEBUG, + "C_DecryptFinal failed:0x%.8X\n", rv); +#endif + } + rv = C_DestroyObject(dec_context->hSession, dec_context->hKey); + if (rv != CKR_OK) { +#ifdef DEBUG + text->utils->log(text->utils->conn, SASL_LOG_DEBUG, + "C_DestroyObject failed:0x%.8X\n", rv); +#endif + } + + rv = C_CloseSession(dec_context->hSession); + if (rv != CKR_OK) { +#ifdef DEBUG + text->utils->log(text->utils->conn, SASL_LOG_DEBUG, + "C_CloseSession failed:0x%.8X\n", rv); +#endif + } + text->utils->free(dec_context); + } + text->cipher_enc_context = NULL; + text->cipher_dec_context = NULL; +} + +static int +dec_rc4_uef(context_t *text, + const char *input, + unsigned inputlen, + unsigned char digest[16], + char *output, + unsigned *outputlen) +{ + CK_RV rv; + uef_context_t *dec_context = + (uef_context_t *)text->cipher_dec_context; + CK_ULONG ulDataLen = *outputlen - MAC_SIZE; + CK_ULONG ulDigestLen = MAC_SIZE; + + rv = C_DecryptUpdate(dec_context->hSession, (CK_BYTE_PTR)input, + inputlen - MAC_SIZE, (CK_BYTE_PTR)output, &ulDataLen); + if (rv != CKR_OK) { +#ifdef DEBUG + text->utils->log(text->utils->conn, SASL_LOG_DEBUG, + "C_DecryptUpdate failed:0x%.8X\n", rv); +#endif + return SASL_FAIL; + } + *outputlen = (unsigned)ulDataLen; + + rv = C_DecryptUpdate(dec_context->hSession, + (CK_BYTE_PTR)input+(inputlen-MAC_SIZE), MAC_SIZE, (CK_BYTE_PTR)digest, + &ulDigestLen); + if (rv != CKR_OK || ulDigestLen != MAC_SIZE) { +#ifdef DEBUG + text->utils->log(text->utils->conn, SASL_LOG_DEBUG, + "C_DecryptUpdate:0x%.8X, digestLen:%d\n", + rv, ulDigestLen); +#endif + return SASL_FAIL; + } + + return SASL_OK; +} + +static int +enc_rc4_uef(context_t *text, + const char *input, + unsigned inputlen, + unsigned char digest[16], + char *output, + unsigned *outputlen) +{ + CK_RV rv; + uef_context_t *enc_context = + (uef_context_t *)text->cipher_enc_context; + CK_ULONG ulDataLen = inputlen; + CK_ULONG ulDigestLen = MAC_SIZE; + + rv = C_EncryptUpdate(enc_context->hSession, (CK_BYTE_PTR)input, inputlen, + (CK_BYTE_PTR)output, &ulDataLen); + if (rv != CKR_OK) { +#ifdef DEBUG + text->utils->log(text->utils->conn, SASL_LOG_DEBUG, + "C_EncryptUpdate failed: 0x%.8X " + "inputlen:%d outputlen:%d\n", + rv, inputlen, ulDataLen); +#endif + return SASL_FAIL; + } + rv = C_EncryptUpdate(enc_context->hSession, (CK_BYTE_PTR)digest, MAC_SIZE, + (CK_BYTE_PTR)output + inputlen, &ulDigestLen); + if (rv != CKR_OK) { +#ifdef DEBUG + text->utils->log(text->utils->conn, SASL_LOG_DEBUG, + "C_EncryptUpdate failed: 0x%.8X ulDigestLen:%d\n", + rv, ulDigestLen); +#endif + return SASL_FAIL; + } + + *outputlen = ulDataLen + ulDigestLen; + + return SASL_OK; +} + +static int +dec_des_uef(context_t *text, + const char *input, + unsigned inputlen, + unsigned char digest[16], + char *output, + unsigned *outputlen) +{ + CK_RV rv; + uef_context_t *dec_context = + (uef_context_t *)text->cipher_dec_context; + CK_ULONG ulDataLen = inputlen; + int padding, p; + + rv = C_DecryptUpdate(dec_context->hSession, (CK_BYTE_PTR)input, + inputlen, (CK_BYTE_PTR)output, &ulDataLen); + if (rv != CKR_OK) { +#ifdef DEBUG + text->utils->log(text->utils->conn, SASL_LOG_DEBUG, + "C_DecryptUpdate failed:0x%.8X\n", rv); +#endif + return SASL_FAIL; + } + if (ulDataLen != inputlen) { +#ifdef DEBUG + text->utils->log(text->utils->conn, SASL_LOG_DEBUG, + "C_DecryptUpdate unexpected data len:%d !=%d\n", + inputlen, ulDataLen); +#endif + return SASL_BUFOVER; + } + + /* now chop off the padding */ + padding = output[inputlen - 11]; + if (padding < 1 || padding > 8) { + /* invalid padding length */ + return SASL_BADMAC; + } + /* verify all padding is correct */ + for (p = 1; p <= padding; p++) { + if (output[inputlen - MAC_SIZE - p] != padding) { + return SASL_BADMAC; + } + } + + /* chop off the padding */ + *outputlen = inputlen - padding - MAC_SIZE; + + /* copy in the HMAC to digest */ + memcpy(digest, output + inputlen - MAC_SIZE, MAC_SIZE); + + return SASL_OK; +} + +static int +enc_des_uef(context_t *text, + const char *input, + unsigned inputlen, + unsigned char digest[16], + char *output, + unsigned *outputlen) +{ + CK_RV rv; + uef_context_t *enc_context = + (uef_context_t *)text->cipher_enc_context; + CK_ULONG ulDataLen; + int paddinglen; + + /* determine padding length */ + paddinglen = 8 - ((inputlen + MAC_SIZE) % 8); + + /* now construct the full stuff to be ciphered */ + memcpy(output, input, inputlen); /* text */ + memset(output+inputlen, paddinglen, paddinglen);/* pad */ + memcpy(output+inputlen+paddinglen, digest, MAC_SIZE); /* hmac */ + + ulDataLen=inputlen+paddinglen+MAC_SIZE; + + rv = C_EncryptUpdate(enc_context->hSession, (CK_BYTE_PTR)output, ulDataLen, + (CK_BYTE_PTR)output, &ulDataLen); + if (rv != CKR_OK) { +#ifdef DEBUG + text->utils->log(text->utils->conn, SASL_LOG_DEBUG, + "C_EncryptUpdate failed: 0x%.8X " + "inputlen:%d outputlen:%d\n", + rv, ulDataLen, ulDataLen); +#endif + return SASL_FAIL; + } + *outputlen = (unsigned)ulDataLen; + + return SASL_OK; +} + +struct digest_cipher uef_ciphers[] = +{ + { "rc4-40", 40, 5, 0x01, &enc_rc4_uef, &dec_rc4_uef, &init_rc4_uef, + &free_uef }, + { "rc4-56", 56, 7, 0x02, &enc_rc4_uef, &dec_rc4_uef, &init_rc4_uef, + &free_uef }, + { "rc4", 128, 16, 0x04, &enc_rc4_uef, &dec_rc4_uef, &init_rc4_uef, + &free_uef }, + { "des", 55, 16, 0x08, &enc_des_uef, &dec_des_uef, &init_des_uef, + &free_uef }, + { "3des", 112, 16, 0x10, &enc_des_uef, &dec_des_uef, &init_3des_uef, + &free_uef }, + { NULL, 0, 0, 0, NULL, NULL, NULL, NULL } +}; + +struct digest_cipher *available_ciphers1 = uef_ciphers; +#endif /* USE_UEF */ + +static int create_layer_keys(context_t *text, + const sasl_utils_t *utils, + HASH key, int keylen, + char enckey[16], char deckey[16]) +{ + MD5_CTX Md5Ctx; + + utils->MD5Init(&Md5Ctx); + utils->MD5Update(&Md5Ctx, key, keylen); + if (text->i_am == SERVER) { + utils->MD5Update(&Md5Ctx, (const unsigned char *) SEALING_SERVER_CLIENT, + strlen(SEALING_SERVER_CLIENT)); + } else { + utils->MD5Update(&Md5Ctx, (const unsigned char *) SEALING_CLIENT_SERVER, + strlen(SEALING_CLIENT_SERVER)); + } + utils->MD5Final((unsigned char *) enckey, &Md5Ctx); + + utils->MD5Init(&Md5Ctx); + utils->MD5Update(&Md5Ctx, key, keylen); + if (text->i_am != SERVER) { + utils->MD5Update(&Md5Ctx, (const unsigned char *)SEALING_SERVER_CLIENT, + strlen(SEALING_SERVER_CLIENT)); + } else { + utils->MD5Update(&Md5Ctx, (const unsigned char *)SEALING_CLIENT_SERVER, + strlen(SEALING_CLIENT_SERVER)); + } + utils->MD5Final((unsigned char *) deckey, &Md5Ctx); + + /* create integrity keys */ + /* sending */ + utils->MD5Init(&Md5Ctx); + utils->MD5Update(&Md5Ctx, text->HA1, HASHLEN); + if (text->i_am == SERVER) { + utils->MD5Update(&Md5Ctx, (const unsigned char *)SIGNING_SERVER_CLIENT, + strlen(SIGNING_SERVER_CLIENT)); + } else { + utils->MD5Update(&Md5Ctx, (const unsigned char *)SIGNING_CLIENT_SERVER, + strlen(SIGNING_CLIENT_SERVER)); + } + utils->MD5Final(text->Ki_send, &Md5Ctx); + + /* receiving */ + utils->MD5Init(&Md5Ctx); + utils->MD5Update(&Md5Ctx, text->HA1, HASHLEN); + if (text->i_am != SERVER) { + utils->MD5Update(&Md5Ctx, (const unsigned char *)SIGNING_SERVER_CLIENT, + strlen(SIGNING_SERVER_CLIENT)); + } else { + utils->MD5Update(&Md5Ctx, (const unsigned char *)SIGNING_CLIENT_SERVER, + strlen(SIGNING_CLIENT_SERVER)); + } + utils->MD5Final(text->Ki_receive, &Md5Ctx); + + return SASL_OK; +} + +static const unsigned short version = 1; + +/* len, CIPHER(Kc, {msg, pag, HMAC(ki, {SeqNum, msg})[0..9]}), x0001, SeqNum */ + +static int +digestmd5_privacy_encode(void *context, + const struct iovec *invec, + unsigned numiov, + const char **output, + unsigned *outputlen) +{ + context_t *text = (context_t *) context; + int tmp; + unsigned int tmpnum; + unsigned short int tmpshort; + int ret; + char *out; + unsigned char digest[16]; + struct buffer_info *inblob, bufinfo; + + if(!context || !invec || !numiov || !output || !outputlen) { + PARAMERROR(text->utils); + return SASL_BADPARAM; + } + + if (numiov > 1) { + ret = _plug_iovec_to_buf(text->utils, invec, numiov, &text->enc_in_buf); + if (ret != SASL_OK) return ret; + inblob = text->enc_in_buf; + } else { + /* avoid the data copy */ + bufinfo.data = invec[0].iov_base; + bufinfo.curlen = invec[0].iov_len; + inblob = &bufinfo; + } + + /* make sure the output buffer is big enough for this blob */ + ret = _plug_buf_alloc(text->utils, &(text->encode_buf), + &(text->encode_buf_len), + (4 + /* for length */ + inblob->curlen + /* for content */ + 10 + /* for MAC */ + 8 + /* maximum pad */ + 6 + /* for padding */ + 1)); /* trailing null */ + if(ret != SASL_OK) return ret; + + /* skip by the length for now */ + out = (text->encode_buf)+4; + + /* construct (seqnum, msg) */ + /* We can just use the output buffer because it's big enough */ + tmpnum = htonl(text->seqnum); + memcpy(text->encode_buf, &tmpnum, 4); + memcpy(text->encode_buf + 4, inblob->data, inblob->curlen); + + /* HMAC(ki, (seqnum, msg) ) */ + text->utils->hmac_md5((const unsigned char *) text->encode_buf, + inblob->curlen + 4, + text->Ki_send, HASHLEN, digest); + + /* calculate the encrypted part */ + text->cipher_enc(text, inblob->data, inblob->curlen, + digest, out, outputlen); + out+=(*outputlen); + + /* copy in version */ + tmpshort = htons(version); + memcpy(out, &tmpshort, 2); /* 2 bytes = version */ + + out+=2; + (*outputlen)+=2; /* for version */ + + /* put in seqnum */ + tmpnum = htonl(text->seqnum); + memcpy(out, &tmpnum, 4); /* 4 bytes = seq # */ + + (*outputlen)+=4; /* for seqnum */ + + /* put the 1st 4 bytes in */ + tmp=htonl(*outputlen); + memcpy(text->encode_buf, &tmp, 4); + + (*outputlen)+=4; + + *output = text->encode_buf; + text->seqnum++; + + return SASL_OK; +} + +static int +digestmd5_privacy_decode_once(void *context, + const char **input, + unsigned *inputlen, + char **output, + unsigned *outputlen) +{ + context_t *text = (context_t *) context; + unsigned int tocopy; + unsigned diff; + int result; + unsigned char digest[16]; + int tmpnum; + int lup; + + if (text->needsize>0) /* 4 bytes for how long message is */ + { + /* if less than 4 bytes just copy those we have into text->size */ + if (*inputlen<4) + tocopy=*inputlen; + else + tocopy=4; + + if (tocopy>text->needsize) + tocopy=text->needsize; + + memcpy(text->sizebuf+4-text->needsize, *input, tocopy); + text->needsize-=tocopy; + + *input+=tocopy; + *inputlen-=tocopy; + + if (text->needsize==0) /* got all of size */ + { + memcpy(&(text->size), text->sizebuf, 4); + text->cursize=0; + text->size=ntohl(text->size); + + if (text->size > text->in_maxbuf) { + return SASL_FAIL; /* too big probably error */ + } + + if(!text->buffer) + text->buffer=text->utils->malloc(text->size+5); + else + text->buffer=text->utils->realloc(text->buffer, + text->size+5); + if (text->buffer == NULL) return SASL_NOMEM; + } + + *outputlen=0; + *output=NULL; + if (*inputlen==0) /* have to wait until next time for data */ + return SASL_OK; + + if (text->size==0) /* should never happen */ + return SASL_FAIL; + } + + diff=text->size - text->cursize; /* bytes need for full message */ + + if (! text->buffer) + return SASL_FAIL; + + if (*inputlen < diff) /* not enough for a decode */ + { + memcpy(text->buffer+text->cursize, *input, *inputlen); + text->cursize+=*inputlen; + *inputlen=0; + *outputlen=0; + *output=NULL; + return SASL_OK; + } else { + memcpy(text->buffer+text->cursize, *input, diff); + *input+=diff; + *inputlen-=diff; + } + + { + unsigned short ver; + unsigned int seqnum; + unsigned char checkdigest[16]; + + result = _plug_buf_alloc(text->utils, &text->decode_once_buf, + &text->decode_once_buf_len, + text->size-6); + if (result != SASL_OK) + return result; + + *output = text->decode_once_buf; + *outputlen = *inputlen; + + result=text->cipher_dec(text,text->buffer,text->size-6,digest, + *output, outputlen); + + if (result!=SASL_OK) + return result; + + { + int i; + for(i=10; i; i--) { + memcpy(&ver, text->buffer+text->size-i,2); + ver=ntohs(ver); + } + } + + /* check the version number */ + memcpy(&ver, text->buffer+text->size-6, 2); + ver=ntohs(ver); + if (ver != version) + { +#ifdef _INTEGRATED_SOLARIS_ + text->utils->seterror(text->utils->conn, 0, + gettext("Wrong Version")); +#else + text->utils->seterror(text->utils->conn, 0, "Wrong Version"); +#endif /* _INTEGRATED_SOLARIS_ */ + return SASL_FAIL; + } + + /* check the CMAC */ + + /* construct (seqnum, msg) */ + result = _plug_buf_alloc(text->utils, &text->decode_tmp_buf, + &text->decode_tmp_buf_len, *outputlen + 4); + if(result != SASL_OK) return result; + + tmpnum = htonl(text->rec_seqnum); + memcpy(text->decode_tmp_buf, &tmpnum, 4); + memcpy(text->decode_tmp_buf + 4, *output, *outputlen); + + /* HMAC(ki, (seqnum, msg) ) */ + text->utils->hmac_md5((const unsigned char *) text->decode_tmp_buf, + (*outputlen) + 4, + text->Ki_receive, HASHLEN, checkdigest); + + /* now check it */ + for (lup=0;lup<10;lup++) + if (checkdigest[lup]!=digest[lup]) + { +#ifdef _SUN_SDK_ + text->utils->log(text->utils->conn, SASL_LOG_ERR, + "CMAC doesn't match at byte %d!", lup); + return SASL_BADMAC; +#else + text->utils->seterror(text->utils->conn, 0, + "CMAC doesn't match at byte %d!", lup); + return SASL_FAIL; +#endif /* _SUN_SDK_ */ + } + + /* check the sequence number */ + memcpy(&seqnum, text->buffer+text->size-4,4); + seqnum=ntohl(seqnum); + + if (seqnum!=text->rec_seqnum) + { +#ifdef _SUN_SDK_ + text->utils->log(text->utils->conn, SASL_LOG_ERR, + "Incorrect Sequence Number"); +#else + text->utils->seterror(text->utils->conn, 0, + "Incorrect Sequence Number"); +#endif /* _SUN_SDK_ */ + return SASL_FAIL; + } + + text->rec_seqnum++; /* now increment it */ + } + + text->needsize=4; + + return SASL_OK; +} + +static int digestmd5_privacy_decode(void *context, + const char *input, unsigned inputlen, + const char **output, unsigned *outputlen) +{ + context_t *text = (context_t *) context; + int ret; + + ret = _plug_decode(text->utils, context, input, inputlen, + &text->decode_buf, &text->decode_buf_len, outputlen, + digestmd5_privacy_decode_once); + + *output = text->decode_buf; + + return ret; +} + +static int +digestmd5_integrity_encode(void *context, + const struct iovec *invec, + unsigned numiov, + const char **output, + unsigned *outputlen) +{ + context_t *text = (context_t *) context; + unsigned char MAC[16]; + unsigned int tmpnum; + unsigned short int tmpshort; + struct buffer_info *inblob, bufinfo; + int ret; + + if(!context || !invec || !numiov || !output || !outputlen) { + PARAMERROR( text->utils ); + return SASL_BADPARAM; + } + + if (numiov > 1) { + ret = _plug_iovec_to_buf(text->utils, invec, numiov, + &text->enc_in_buf); + if (ret != SASL_OK) return ret; + inblob = text->enc_in_buf; + } else { + /* avoid the data copy */ + bufinfo.data = invec[0].iov_base; + bufinfo.curlen = invec[0].iov_len; + inblob = &bufinfo; + } + + /* construct output */ + *outputlen = 4 + inblob->curlen + 16; + + ret = _plug_buf_alloc(text->utils, &(text->encode_buf), + &(text->encode_buf_len), *outputlen); + if(ret != SASL_OK) return ret; + + /* construct (seqnum, msg) */ + /* we can just use the output buffer */ + tmpnum = htonl(text->seqnum); + memcpy(text->encode_buf, &tmpnum, 4); + memcpy(text->encode_buf + 4, inblob->data, inblob->curlen); + + /* HMAC(ki, (seqnum, msg) ) */ +#ifdef _SUN_SDK_ + text->utils->hmac_md5((unsigned char *)text->encode_buf, + inblob->curlen + 4, + text->Ki_send, HASHLEN, MAC); +#else + text->utils->hmac_md5(text->encode_buf, inblob->curlen + 4, + text->Ki_send, HASHLEN, MAC); +#endif /* _SUN_SDK_ */ + + /* create MAC */ + tmpshort = htons(version); + memcpy(MAC + 10, &tmpshort, MAC_OFFS); /* 2 bytes = version */ + + tmpnum = htonl(text->seqnum); + memcpy(MAC + 12, &tmpnum, 4); /* 4 bytes = sequence number */ + + /* copy into output */ + tmpnum = htonl((*outputlen) - 4); + + /* length of message in network byte order */ + memcpy(text->encode_buf, &tmpnum, 4); + /* the message text */ + memcpy(text->encode_buf + 4, inblob->data, inblob->curlen); + /* the MAC */ + memcpy(text->encode_buf + 4 + inblob->curlen, MAC, 16); + + text->seqnum++; /* add one to sequence number */ + + *output = text->encode_buf; + + return SASL_OK; +} + +static int +create_MAC(context_t * text, + 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; + + ret = _plug_buf_alloc(text->utils, &(text->MAC_buf), + &(text->MAC_buf_len), inputlen + 4); + if(ret != SASL_OK) return ret; + + /* construct (seqnum, msg) */ + tmpnum = htonl(seqnum); + memcpy(text->MAC_buf, &tmpnum, 4); + memcpy(text->MAC_buf + 4, input, inputlen); + + /* HMAC(ki, (seqnum, msg) ) */ +#ifdef _SUN_SDK_ + text->utils->hmac_md5((unsigned char *)text->MAC_buf, inputlen + 4, + text->Ki_receive, HASHLEN, + MAC); +#else + text->utils->hmac_md5(text->MAC_buf, inputlen + 4, + text->Ki_receive, HASHLEN, + MAC); +#endif /* _SUN_SDK_ */ + + /* create MAC */ + tmpshort = htons(version); + memcpy(MAC + 10, &tmpshort, 2); /* 2 bytes = version */ + + tmpnum = htonl(seqnum); + memcpy(MAC + 12, &tmpnum, 4); /* 4 bytes = sequence number */ + + return SASL_OK; +} + +static int +check_integrity(context_t * text, + char *buf, int bufsize, + char **output, unsigned *outputlen) +{ + unsigned char MAC[16]; + int result; + + result = create_MAC(text, buf, bufsize - 16, text->rec_seqnum, MAC); + if (result != SASL_OK) + return result; + + /* make sure the MAC is right */ + if (strncmp((char *) MAC, buf + bufsize - 16, 16) != 0) + { +#ifdef _SUN_SDK_ + text->utils->log(text->utils->conn, SASL_LOG_ERR, + "MAC doesn't match"); + return SASL_BADMAC; +#else + text->utils->seterror(text->utils->conn, 0, "MAC doesn't match"); + return SASL_FAIL; +#endif /* _SUN_SDK_ */ + } + + text->rec_seqnum++; + + /* ok make output message */ + result = _plug_buf_alloc(text->utils, &text->decode_once_buf, + &text->decode_once_buf_len, + bufsize - 15); + if (result != SASL_OK) + return result; + + *output = text->decode_once_buf; + memcpy(*output, buf, bufsize - 16); + *outputlen = bufsize - 16; + (*output)[*outputlen] = 0; + + return SASL_OK; +} + +static int +digestmd5_integrity_decode_once(void *context, + const char **input, + unsigned *inputlen, + char **output, + unsigned *outputlen) +{ + context_t *text = (context_t *) context; + unsigned int tocopy; + unsigned diff; + int result; + + if (text->needsize > 0) { /* 4 bytes for how long message is */ + /* + * if less than 4 bytes just copy those we have into text->size + */ + if (*inputlen < 4) + tocopy = *inputlen; + else + tocopy = 4; + + if (tocopy > text->needsize) + tocopy = text->needsize; + + memcpy(text->sizebuf + 4 - text->needsize, *input, tocopy); + text->needsize -= tocopy; + + *input += tocopy; + *inputlen -= tocopy; + + if (text->needsize == 0) { /* got all of size */ + memcpy(&(text->size), text->sizebuf, 4); + text->cursize = 0; + text->size = ntohl(text->size); + + if (text->size > text->in_maxbuf) + return SASL_FAIL; /* too big probably error */ + + if(!text->buffer) + text->buffer=text->utils->malloc(text->size+5); + else + text->buffer=text->utils->realloc(text->buffer,text->size+5); + if (text->buffer == NULL) return SASL_NOMEM; + } + *outputlen = 0; + *output = NULL; + if (*inputlen == 0) /* have to wait until next time for data */ + return SASL_OK; + + if (text->size == 0) /* should never happen */ + return SASL_FAIL; + } + diff = text->size - text->cursize; /* bytes need for full message */ + + if(! text->buffer) + return SASL_FAIL; + + if (*inputlen < diff) { /* not enough for a decode */ + memcpy(text->buffer + text->cursize, *input, *inputlen); + text->cursize += *inputlen; + *inputlen = 0; + *outputlen = 0; + *output = NULL; + return SASL_OK; + } else { + memcpy(text->buffer + text->cursize, *input, diff); + *input += diff; + *inputlen -= diff; + } + + result = check_integrity(text, text->buffer, text->size, + output, outputlen); + if (result != SASL_OK) + return result; + + /* Reset State */ + text->needsize = 4; + + return SASL_OK; +} + +static int digestmd5_integrity_decode(void *context, + const char *input, unsigned inputlen, + const char **output, unsigned *outputlen) +{ + context_t *text = (context_t *) context; + int ret; + + ret = _plug_decode(text->utils, context, input, inputlen, + &text->decode_buf, &text->decode_buf_len, outputlen, + digestmd5_integrity_decode_once); + + *output = text->decode_buf; + + return ret; +} + +static void +digestmd5_common_mech_dispose(void *conn_context, const sasl_utils_t *utils) +{ + context_t *text = (context_t *) conn_context; + + if (!text || !utils) return; + + if (text->authid) utils->free(text->authid); + if (text->realm) utils->free(text->realm); + if (text->nonce) utils->free(text->nonce); + if (text->cnonce) utils->free(text->cnonce); + + if (text->cipher_free) text->cipher_free(text); + + /* free the stuff in the context */ + if (text->response_value) utils->free(text->response_value); + + if (text->buffer) utils->free(text->buffer); + if (text->encode_buf) utils->free(text->encode_buf); + if (text->decode_buf) utils->free(text->decode_buf); + if (text->decode_once_buf) utils->free(text->decode_once_buf); + if (text->decode_tmp_buf) utils->free(text->decode_tmp_buf); + if (text->out_buf) utils->free(text->out_buf); + if (text->MAC_buf) utils->free(text->MAC_buf); + + if (text->enc_in_buf) { + if (text->enc_in_buf->data) utils->free(text->enc_in_buf->data); + utils->free(text->enc_in_buf); + } + + utils->free(conn_context); +} + +static void +clear_reauth_entry(reauth_entry_t *reauth, enum Context_type type, + const sasl_utils_t *utils) +{ + if (!reauth) return; + + if (reauth->authid) utils->free(reauth->authid); + if (reauth->realm) utils->free(reauth->realm); + if (reauth->nonce) utils->free(reauth->nonce); + if (reauth->cnonce) utils->free(reauth->cnonce); + + if (type == CLIENT) { + if (reauth->u.c.serverFQDN) utils->free(reauth->u.c.serverFQDN); + } + + memset(reauth, 0, sizeof(reauth_entry_t)); +} + +static void +digestmd5_common_mech_free(void *glob_context, const sasl_utils_t *utils) +{ + reauth_cache_t *reauth_cache = (reauth_cache_t *) glob_context; + size_t n; + + if (!reauth_cache) return; + + for (n = 0; n < reauth_cache->size; n++) + clear_reauth_entry(&reauth_cache->e[n], reauth_cache->i_am, utils); + if (reauth_cache->e) utils->free(reauth_cache->e); + + if (reauth_cache->mutex) utils->mutex_free(reauth_cache->mutex); + + utils->free(reauth_cache); +} + +/***************************** Server Section *****************************/ + +typedef struct server_context { + context_t common; + + time_t timestamp; + int stale; /* last nonce is stale */ + sasl_ssf_t limitssf, requiressf; /* application defined bounds */ +} server_context_t; + +static void +DigestCalcHA1FromSecret(context_t * text, + const sasl_utils_t * utils, + HASH HA1, + unsigned char *authorization_id, + unsigned char *pszNonce, + unsigned char *pszCNonce, + HASHHEX SessionKey) +{ + MD5_CTX Md5Ctx; + + /* calculate session key */ + utils->MD5Init(&Md5Ctx); + utils->MD5Update(&Md5Ctx, HA1, HASHLEN); + utils->MD5Update(&Md5Ctx, COLON, 1); + utils->MD5Update(&Md5Ctx, pszNonce, strlen((char *) pszNonce)); + utils->MD5Update(&Md5Ctx, COLON, 1); + utils->MD5Update(&Md5Ctx, pszCNonce, strlen((char *) pszCNonce)); + if (authorization_id != NULL) { + utils->MD5Update(&Md5Ctx, COLON, 1); + utils->MD5Update(&Md5Ctx, authorization_id, strlen((char *) authorization_id)); + } + utils->MD5Final(HA1, &Md5Ctx); + + CvtHex(HA1, SessionKey); + + + /* save HA1 because we need it to make the privacy and integrity keys */ + memcpy(text->HA1, HA1, sizeof(HASH)); +} + +static char *create_response(context_t * text, + const sasl_utils_t * utils, + unsigned char *nonce, + unsigned int ncvalue, + unsigned char *cnonce, + char *qop, + char *digesturi, + HASH Secret, + char *authorization_id, + char **response_value) +{ + HASHHEX SessionKey; + HASHHEX HEntity = "00000000000000000000000000000000"; + HASHHEX Response; + char *result; + + if (qop == NULL) + qop = "auth"; + + DigestCalcHA1FromSecret(text, + utils, + Secret, + (unsigned char *) authorization_id, + nonce, + cnonce, + SessionKey); + + DigestCalcResponse(utils, + 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 */ + ); + + result = utils->malloc(HASHHEXLEN + 1); +#ifdef _SUN_SDK_ + if (result == NULL) + return NULL; +#endif /* _SUN_SDK_ */ +/* TODO */ + memcpy(result, Response, HASHHEXLEN); + result[HASHHEXLEN] = 0; + + /* response_value (used for reauth i think */ + if (response_value != NULL) { + DigestCalcResponse(utils, + 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 */ + ); + + *response_value = utils->malloc(HASHHEXLEN + 1); + if (*response_value == NULL) + return NULL; + memcpy(*response_value, Response, HASHHEXLEN); + (*response_value)[HASHHEXLEN] = 0; + } + return result; +} + +static int +get_server_realm(sasl_server_params_t * params, + char **realm) +{ + /* look at user realm first */ + if (params->user_realm != NULL) { + if(params->user_realm[0] != '\0') { + *realm = (char *) params->user_realm; + } else { + /* Catch improperly converted apps */ +#ifdef _SUN_SDK_ + params->utils->log(params->utils->conn, SASL_LOG_ERR, + "user_realm is an empty string!"); +#else + params->utils->seterror(params->utils->conn, 0, + "user_realm is an empty string!"); +#endif /* _SUN_SDK_ */ + return SASL_BADPARAM; + } + } else if (params->serverFQDN != NULL) { + *realm = (char *) params->serverFQDN; + } else { +#ifdef _SUN_SDK_ + params->utils->log(params->utils->conn, SASL_LOG_ERR, + "no way to obtain domain"); +#else + params->utils->seterror(params->utils->conn, 0, + "no way to obtain domain"); +#endif /* _SUN_SDK_ */ + return SASL_FAIL; + } + + return SASL_OK; +} + +/* + * Convert hex string to int + */ +static int htoi(unsigned char *hexin, unsigned int *res) +{ + int lup, inlen; + inlen = strlen((char *) hexin); + + *res = 0; + for (lup = 0; lup < inlen; lup++) { + switch (hexin[lup]) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + *res = (*res << 4) + (hexin[lup] - '0'); + break; + + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + *res = (*res << 4) + (hexin[lup] - 'a' + 10); + break; + + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + *res = (*res << 4) + (hexin[lup] - 'A' + 10); + break; + + default: + return SASL_BADPARAM; + } + + } + + return SASL_OK; +} + +static int digestmd5_server_mech_new(void *glob_context, + sasl_server_params_t * sparams, + const char *challenge __attribute__((unused)), + unsigned challen __attribute__((unused)), + void **conn_context) +{ + context_t *text; + + /* holds state are in -- allocate server size */ + text = sparams->utils->malloc(sizeof(server_context_t)); + if (text == NULL) + return SASL_NOMEM; + memset(text, 0, sizeof(server_context_t)); + + text->state = 1; + text->i_am = SERVER; + text->reauth = glob_context; + + *conn_context = text; + return SASL_OK; +} + +static int +digestmd5_server_mech_step1(server_context_t *stext, + sasl_server_params_t *sparams, + const char *clientin __attribute__((unused)), + unsigned clientinlen __attribute__((unused)), + const char **serverout, + unsigned *serveroutlen, + sasl_out_params_t * oparams __attribute__((unused))) +{ + context_t *text = (context_t *) stext; + int result; + char *realm; + unsigned char *nonce; + char *charset = "utf-8"; + char qop[1024], cipheropts[1024]; + struct digest_cipher *cipher; + unsigned resplen; + int added_conf = 0; + char maxbufstr[64]; + + sparams->utils->log(sparams->utils->conn, SASL_LOG_DEBUG, + "DIGEST-MD5 server step 1"); + + /* get realm */ + result = get_server_realm(sparams, &realm); + if(result != SASL_OK) return result; + + /* what options should we offer the client? */ + qop[0] = '\0'; + cipheropts[0] = '\0'; + if (stext->requiressf == 0) { + if (*qop) strcat(qop, ","); + strcat(qop, "auth"); + } + if (stext->requiressf <= 1 && stext->limitssf >= 1) { + if (*qop) strcat(qop, ","); + strcat(qop, "auth-int"); + } + +#ifdef USE_UEF_SERVER + cipher = available_ciphers1; +#else + cipher = available_ciphers; +#endif + while (cipher->name) { + /* do we allow this particular cipher? */ + if (stext->requiressf <= cipher->ssf && + stext->limitssf >= cipher->ssf) { + if (!added_conf) { + if (*qop) strcat(qop, ","); + strcat(qop, "auth-conf"); + added_conf = 1; + } +#ifdef _SUN_SDK_ + if(strlen(cipheropts) + strlen(cipher->name) + 1 >= + sizeof (cipheropts)) { + sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR, + "internal error: cipheropts too big"); + return SASL_FAIL; + } +#endif /* _SUN_SDK_ */ + if (*cipheropts) strcat(cipheropts, ","); + strcat(cipheropts, cipher->name); + } + 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_ */ + nonce = create_nonce(sparams->utils); + if (nonce == NULL) { +#ifdef _SUN_SDK_ + /* Note typo below */ + sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR, + "internal error: failed creating a nonce"); +#else + SETERROR(sparams->utils, "internal erorr: failed creating a nonce"); +#endif /* _SUN_SDK_ */ + return SASL_FAIL; + } + +#ifdef _SUN_SDK_ + resplen = strlen((char *)nonce) + strlen("nonce") + 5; +#else + resplen = strlen(nonce) + strlen("nonce") + 5; +#endif /* _SUN_SDK_ */ + result = _plug_buf_alloc(sparams->utils, &(text->out_buf), + &(text->out_buf_len), resplen); +#ifdef _SUN_SDK_ + if(result != SASL_OK) { + sparams->utils->free(nonce); + return result; + } +#else + if(result != SASL_OK) return result; +#endif /* _SUN_SDK_ */ + + sprintf(text->out_buf, "nonce=\"%s\"", nonce); + + /* add to challenge; if we chose not to specify a realm, we won't + * send one to the client */ + if (realm && add_to_challenge(sparams->utils, + &text->out_buf, &text->out_buf_len, &resplen, + "realm", (unsigned char *) realm, + TRUE) != SASL_OK) { +#ifdef _SUN_SDK_ + sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR, + "internal error: add_to_challenge failed"); + sparams->utils->free(nonce); +#else + SETERROR(sparams->utils, "internal error: add_to_challenge failed"); +#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 */ + if (add_to_challenge(sparams->utils, + &text->out_buf, &text->out_buf_len, &resplen, + "qop", + (unsigned char *) qop, TRUE) != SASL_OK) { +#ifdef _SUN_SDK_ + sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR, + "internal error: add_to_challenge 3 failed"); + sparams->utils->free(nonce); +#else + SETERROR(sparams->utils, "internal error: add_to_challenge 3 failed"); +#endif /* _SUN_SDK_ */ + return SASL_FAIL; + } + + /* + * Cipheropts - list of ciphers server supports + */ + /* add cipher-opts to challenge; only add if there are some */ + if (strcmp(cipheropts,"")!=0) + { + if (add_to_challenge(sparams->utils, + &text->out_buf, &text->out_buf_len, &resplen, + "cipher", (unsigned char *) cipheropts, + TRUE) != SASL_OK) { +#ifdef _SUN_SDK_ + sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR, + "internal error: add_to_challenge 4 failed"); + sparams->utils->free(nonce); +#else + SETERROR(sparams->utils, + "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 */ + if (stext->stale && + add_to_challenge(sparams->utils, + &text->out_buf, &text->out_buf_len, &resplen, +#ifdef _SUN_SDK_ + "stale", (unsigned char *)"true", FALSE) != SASL_OK) { + sparams->utils->free(nonce); + sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR, + "internal error: add_to_challenge failed"); +#else + "stale", "true", FALSE) != SASL_OK) { + SETERROR(sparams->utils, "internal error: add_to_challenge failed"); +#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. + */ + if(sparams->props.maxbufsize) { + snprintf(maxbufstr, sizeof(maxbufstr), "%d", + sparams->props.maxbufsize); + if (add_to_challenge(sparams->utils, + &text->out_buf, &text->out_buf_len, &resplen, + "maxbuf", + (unsigned char *) maxbufstr, FALSE) != SASL_OK) { +#ifdef _SUN_SDK_ + sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR, + "internal error: add_to_challenge 5 failed"); +#else + SETERROR(sparams->utils, + "internal error: add_to_challenge 5 failed"); +#endif /* _SUN_SDK_ */ + return SASL_FAIL; + } + } + + + if (add_to_challenge(sparams->utils, + &text->out_buf, &text->out_buf_len, &resplen, + "charset", + (unsigned char *) charset, FALSE) != SASL_OK) { +#ifdef _SUN_SDK_ + sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR, + "internal error: add_to_challenge 6 failed"); + sparams->utils->free(nonce); +#else + SETERROR(sparams->utils, "internal error: add_to_challenge 6 failed"); +#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" + */ + + if (add_to_challenge(sparams->utils, + &text->out_buf, &text->out_buf_len, &resplen, + "algorithm", + (unsigned char *) "md5-sess", FALSE)!=SASL_OK) { +#ifdef _SUN_SDK_ + sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR, + "internal error: add_to_challenge 7 failed"); + sparams->utils->free(nonce); +#else + SETERROR(sparams->utils, "internal error: add_to_challenge 7 failed"); +#endif /* _SUN_SDK_ */ + return SASL_FAIL; + } + + /* + * The size of a digest-challenge MUST be less than 2048 bytes!!! + */ + if (*serveroutlen > 2048) { +#ifdef _SUN_SDK_ + sparams->utils->free(nonce); + sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR, + "internal error: challenge larger than 2048 bytes"); +#else + SETERROR(sparams->utils, + "internal error: challenge larger than 2048 bytes"); +#endif /* _SUN_SDK_ */ + return SASL_FAIL; + } + + text->authid = NULL; + _plug_strdup(sparams->utils, realm, &text->realm, NULL); + text->nonce = nonce; + text->nonce_count = 1; + text->cnonce = NULL; + stext->timestamp = time(0); + + *serveroutlen = strlen(text->out_buf); + *serverout = text->out_buf; + + text->state = 2; + + return SASL_CONTINUE; +} + +static int +digestmd5_server_mech_step2(server_context_t *stext, + sasl_server_params_t *sparams, + const char *clientin, + unsigned clientinlen, + const char **serverout, + unsigned *serveroutlen, + sasl_out_params_t * oparams) +{ + context_t *text = (context_t *) stext; + /* verify digest */ + sasl_secret_t *sec = NULL; + int result; + char *serverresponse = NULL; + char *username = NULL; + char *authorization_id = NULL; + char *realm = NULL; + unsigned char *nonce = NULL, *cnonce = NULL; + unsigned int noncecount = 0; + char *qop = NULL; + char *digesturi = NULL; + char *response = NULL; + + /* setting the default value (65536) */ + unsigned int client_maxbuf = 65536; + int maxbuf_count = 0; /* How many maxbuf instaces was found */ + + char *charset = NULL; + char *cipher = NULL; + unsigned int n=0; + + HASH A1; + + /* password prop_request */ + const char *password_request[] = { SASL_AUX_PASSWORD, + "*cmusaslsecretDIGEST-MD5", + NULL }; + unsigned len; + struct propval auxprop_values[2]; + + /* can we mess with clientin? copy it to be safe */ + char *in_start = NULL; + char *in = NULL; + + sparams->utils->log(sparams->utils->conn, SASL_LOG_DEBUG, + "DIGEST-MD5 server step 2"); + + in = sparams->utils->malloc(clientinlen + 1); +#ifdef _SUN_SDK_ + if (!in) return SASL_NOMEM; +#endif /* _SUN_SDK_ */ + + memcpy(in, clientin, clientinlen); + in[clientinlen] = 0; + + in_start = in; + + + /* parse what we got */ + while (in[0] != '\0') { + char *name = NULL, *value = NULL; + get_pair(&in, &name, &value); + + if (name == NULL) + break; + + /* Extracting parameters */ + + /* + * digest-response = 1#( username | realm | nonce | cnonce | + * nonce-count | qop | digest-uri | response | maxbuf | charset | + * cipher | auth-param ) + */ + + if (strcasecmp(name, "username") == 0) { + _plug_strdup(sparams->utils, value, &username, NULL); + } else if (strcasecmp(name, "authzid") == 0) { + _plug_strdup(sparams->utils, value, &authorization_id, NULL); + } else if (strcasecmp(name, "cnonce") == 0) { + _plug_strdup(sparams->utils, value, (char **) &cnonce, NULL); + } else if (strcasecmp(name, "nc") == 0) { + if (htoi((unsigned char *) value, &noncecount) != SASL_OK) { +#ifdef _SUN_SDK_ + sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR, + "error converting hex to int"); +#else + SETERROR(sparams->utils, + "error converting hex to int"); +#endif /* _SUN_SDK_ */ + result = SASL_BADAUTH; + goto FreeAllMem; + } + } else if (strcasecmp(name, "realm") == 0) { + if (realm) { +#ifdef _SUN_SDK_ + sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR, + "duplicate realm: authentication aborted"); +#else + SETERROR(sparams->utils, + "duplicate realm: authentication aborted"); +#endif /* _SUN_SDK_ */ + result = SASL_FAIL; + goto FreeAllMem; + } + _plug_strdup(sparams->utils, value, &realm, NULL); + } else if (strcasecmp(name, "nonce") == 0) { + _plug_strdup(sparams->utils, value, (char **) &nonce, NULL); + } else if (strcasecmp(name, "qop") == 0) { + _plug_strdup(sparams->utils, value, &qop, NULL); + } else if (strcasecmp(name, "digest-uri") == 0) { + size_t service_len; + + /* + * digest-uri-value = serv-type "/" host [ "/" serv-name ] + */ + + _plug_strdup(sparams->utils, value, &digesturi, NULL); + + /* verify digest-uri format */ + + /* make sure it's the service that we're expecting */ + service_len = strlen(sparams->service); + if (strncasecmp(digesturi, sparams->service, service_len) || + digesturi[service_len] != '/') { + result = SASL_BADAUTH; +#ifdef _SUN_SDK_ + sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR, + "bad digest-uri: doesn't match service"); +#else + SETERROR(sparams->utils, + "bad digest-uri: doesn't match service"); +#endif /* _SUN_SDK_ */ + goto FreeAllMem; + } + + /* xxx we don't verify the hostname component */ + + } else if (strcasecmp(name, "response") == 0) { + _plug_strdup(sparams->utils, value, &response, NULL); + } else if (strcasecmp(name, "cipher") == 0) { + _plug_strdup(sparams->utils, value, &cipher, NULL); + } else if (strcasecmp(name, "maxbuf") == 0) { + maxbuf_count++; + if (maxbuf_count != 1) { + result = SASL_BADAUTH; +#ifdef _SUN_SDK_ + sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR, + "duplicate maxbuf: authentication aborted"); +#else + SETERROR(sparams->utils, + "duplicate maxbuf: authentication aborted"); +#endif /* _SUN_SDK_ */ + goto FreeAllMem; + } else if (sscanf(value, "%u", &client_maxbuf) != 1) { + result = SASL_BADAUTH; +#ifdef _SUN_SDK_ + sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR, + "invalid maxbuf parameter"); +#else + SETERROR(sparams->utils, "invalid maxbuf parameter"); +#endif /* _SUN_SDK_ */ + goto FreeAllMem; + } else { + if (client_maxbuf <= 16) { + result = SASL_BADAUTH; +#ifdef _SUN_SDK_ + sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR, + "maxbuf parameter too small"); +#else + SETERROR(sparams->utils, + "maxbuf parameter too small"); +#endif /* _SUN_SDK_ */ + goto FreeAllMem; + } + } + } else if (strcasecmp(name, "charset") == 0) { + if (strcasecmp(value, "utf-8") != 0) { +#ifdef _SUN_SDK_ + sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR, + "client doesn't support UTF-8"); +#else + SETERROR(sparams->utils, "client doesn't support UTF-8"); +#endif /* _SUN_SDK_ */ + result = SASL_FAIL; + goto FreeAllMem; + } + _plug_strdup(sparams->utils, value, &charset, NULL); + } else { + sparams->utils->log(sparams->utils->conn, SASL_LOG_DEBUG, + "DIGEST-MD5 unrecognized pair %s/%s: ignoring", + name, value); + } + } + + /* + * 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 */ + if ((username == NULL) || + (nonce == NULL) || + (noncecount == 0) || + (cnonce == NULL) || + (digesturi == NULL) || + (response == NULL)) { +#ifdef _SUN_SDK_ + sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR, + "required parameters missing"); +#else + SETERROR(sparams->utils, "required parameters missing"); +#endif /* _SUN_SDK_ */ + result = SASL_BADAUTH; + goto FreeAllMem; + } + + if (text->state == 1) { + unsigned val = hash(username) % text->reauth->size; + + /* reauth attempt, see if we have any info for this user */ + if (sparams->utils->mutex_lock(text->reauth->mutex) == SASL_OK) { /* LOCK */ + if (text->reauth->e[val].authid && + !strcmp(username, text->reauth->e[val].authid)) { + + _plug_strdup(sparams->utils, text->reauth->e[val].realm, + &text->realm, NULL); +#ifdef _SUN_SDK_ + _plug_strdup(sparams->utils, (char *)text->reauth->e[val].nonce, + (char **) &text->nonce, NULL); +#else + _plug_strdup(sparams->utils, text->reauth->e[val].nonce, + (char **) &text->nonce, NULL); +#endif /* _SUN_SDK_ */ + text->nonce_count = ++text->reauth->e[val].nonce_count; +#ifdef _SUN_SDK_ + _plug_strdup(sparams->utils, (char *)text->reauth->e[val].cnonce, + (char **) &text->cnonce, NULL); +#else + _plug_strdup(sparams->utils, text->reauth->e[val].cnonce, + (char **) &text->cnonce, NULL); +#endif /* _SUN_SDK_ */ + stext->timestamp = text->reauth->e[val].u.s.timestamp; + } + sparams->utils->mutex_unlock(text->reauth->mutex); /* UNLOCK */ + } + + if (!text->nonce) { + /* we don't have any reauth info, so bail */ + result = SASL_FAIL; + goto FreeAllMem; + } + } + + /* Sanity check the parameters */ +#ifdef _SUN_SDK_ + if ((realm != NULL && text->realm != NULL && + strcmp(realm, text->realm) != 0) || + (realm == NULL && text->realm != NULL) || + (realm != NULL && text->realm == NULL)) { + sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR, + "realm changed: authentication aborted"); +#else + if (strcmp(realm, text->realm) != 0) { + SETERROR(sparams->utils, + "realm changed: authentication aborted"); +#endif /* _SUN_SDK_ */ + result = SASL_BADAUTH; + goto FreeAllMem; + } +#ifdef _SUN_SDK_ + if (strcmp((char *)nonce, (char *) text->nonce) != 0) { + sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR, + "nonce changed: authentication aborted"); +#else + if (strcmp(nonce, (char *) text->nonce) != 0) { + SETERROR(sparams->utils, + "nonce changed: authentication aborted"); +#endif /* _SUN_SKD_ */ + result = SASL_BADAUTH; + goto FreeAllMem; + } + if (noncecount != text->nonce_count) { +#ifdef _SUN_SDK_ + sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR, + "incorrect nonce-count: authentication aborted"); +#else + SETERROR(sparams->utils, + "incorrect nonce-count: authentication aborted"); +#endif /* _SUN_SDK_ */ + result = SASL_BADAUTH; + goto FreeAllMem; + } +#ifdef _SUN_SDK_ + if (text->cnonce && strcmp((char *)cnonce, (char *)text->cnonce) != 0) { + sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR, + "cnonce changed: authentication aborted"); +#else + if (text->cnonce && strcmp(cnonce, text->cnonce) != 0) { + SETERROR(sparams->utils, + "cnonce changed: authentication aborted"); +#endif /* _SUN_SDK_ */ + result = SASL_BADAUTH; + goto FreeAllMem; + } + + result = sparams->utils->prop_request(sparams->propctx, password_request); + if(result != SASL_OK) { +#ifdef _SUN_SDK_ + sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR, + "unable to request user password"); +#else + SETERROR(sparams->utils, "unable to resquest user password"); +#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... */ + result = sparams->canon_user(sparams->utils->conn, + username, 0, SASL_CU_AUTHID, oparams); + if (result != SASL_OK) { +#ifdef _SUN_SDK_ + sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR, + "unable canonify user and get auxprops"); +#else + SETERROR(sparams->utils, "unable canonify user and get auxprops"); +#endif /* _SUN_SDK_ */ + goto FreeAllMem; + } + + if (!authorization_id || !*authorization_id) { + result = sparams->canon_user(sparams->utils->conn, + username, 0, SASL_CU_AUTHZID, oparams); + } else { + result = sparams->canon_user(sparams->utils->conn, + authorization_id, 0, SASL_CU_AUTHZID, + oparams); + } + + if (result != SASL_OK) { +#ifdef _SUN_SDK_ + sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR, + "unable to canonicalize authorization ID"); +#else + SETERROR(sparams->utils, "unable authorization ID"); +#endif /* _SUN_SDK_ */ + goto FreeAllMem; + } + + result = sparams->utils->prop_getnames(sparams->propctx, password_request, + auxprop_values); + if (result < 0 || + ((!auxprop_values[0].name || !auxprop_values[0].values) && + (!auxprop_values[1].name || !auxprop_values[1].values))) { + /* We didn't find this username */ +#ifdef _INTEGRATED_SOLARIS_ + sparams->utils->seterror(sparams->utils->conn, 0, + gettext("no secret in database")); +#else + sparams->utils->seterror(sparams->utils->conn, 0, + "no secret in database"); +#endif /* _INTEGRATED_SOLARIS_ */ + result = SASL_NOUSER; + goto FreeAllMem; + } + + if (auxprop_values[0].name && auxprop_values[0].values) { + len = strlen(auxprop_values[0].values[0]); + if (len == 0) { +#ifdef _INTEGRATED_SOLARIS_ + sparams->utils->seterror(sparams->utils->conn,0, + gettext("empty secret")); +#else + sparams->utils->seterror(sparams->utils->conn,0, + "empty secret"); +#endif /* _INTEGRATED_SOLARIS_ */ + result = SASL_FAIL; + goto FreeAllMem; + } + + sec = sparams->utils->malloc(sizeof(sasl_secret_t) + len); + if (!sec) { +#ifdef _SUN_SDK_ + sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR, + "unable to allocate secret"); +#else + SETERROR(sparams->utils, "unable to allocate secret"); +#endif /* _SUN_SDK_ */ + result = SASL_FAIL; + goto FreeAllMem; + } + + sec->len = len; +#ifdef _SUN_SDK_ + strncpy((char *)sec->data, auxprop_values[0].values[0], len + 1); +#else + strncpy(sec->data, auxprop_values[0].values[0], len + 1); +#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 */ + { + HASH HA1; + +#ifdef _SUN_SDK_ + DigestCalcSecret(sparams->utils, (unsigned char *)username, + (unsigned char *)text->realm, sec->data, + sec->len, HA1); +#else + DigestCalcSecret(sparams->utils, username, + text->realm, sec->data, sec->len, HA1); +#endif /* _SUN_SDK_ */ + + /* + * A1 = { H( { username-value, ":", realm-value, ":", passwd } ), + * ":", nonce-value, ":", cnonce-value } + */ + + memcpy(A1, HA1, HASHLEN); + A1[HASHLEN] = '\0'; + } + + /* We're done with sec now. Let's get rid of it */ + _plug_free_secret(sparams->utils, &sec); + } else if (auxprop_values[1].name && auxprop_values[1].values) { + memcpy(A1, auxprop_values[1].values[0], HASHLEN); + A1[HASHLEN] = '\0'; + } else { +#ifdef _SUN_SDK_ + sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR, + "Have neither type of secret"); +#else + sparams->utils->seterror(sparams->utils->conn, 0, + "Have neither type of secret"); +#endif /* _SUN_SDK_ */ +#ifdef _SUN_SDK_ + result = SASL_FAIL; + goto FreeAllMem; +#else + return SASL_FAIL; +#endif /* _SUN_SDK_ */ + } + + /* defaulting qop to "auth" if not specified */ + if (qop == NULL) { + _plug_strdup(sparams->utils, "auth", &qop, NULL); + } + + /* check which layer/cipher to use */ + if ((!strcasecmp(qop, "auth-conf")) && (cipher != NULL)) { + /* see what cipher was requested */ + struct digest_cipher *cptr; + +#ifdef USE_UEF_SERVER + cptr = available_ciphers1; +#else + cptr = available_ciphers; +#endif + while (cptr->name) { + /* find the cipher requested & make sure it's one we're happy + with by policy */ + if (!strcasecmp(cipher, cptr->name) && + stext->requiressf <= cptr->ssf && + stext->limitssf >= cptr->ssf) { + /* found it! */ + break; + } + cptr++; + } + + if (cptr->name) { + text->cipher_enc = cptr->cipher_enc; + text->cipher_dec = cptr->cipher_dec; + text->cipher_init = cptr->cipher_init; + text->cipher_free = cptr->cipher_free; + oparams->mech_ssf = cptr->ssf; + n = cptr->n; + } else { + /* erg? client requested something we didn't advertise! */ + sparams->utils->log(sparams->utils->conn, SASL_LOG_WARN, + "protocol violation: client requested invalid cipher"); +#ifndef _SUN_SDK_ + SETERROR(sparams->utils, "client requested invalid cipher"); +#endif /* !_SUN_SDK_ */ + /* Mark that we attempted security layer negotiation */ + oparams->mech_ssf = 2; + result = SASL_FAIL; + goto FreeAllMem; + } + + oparams->encode=&digestmd5_privacy_encode; + oparams->decode=&digestmd5_privacy_decode; + } else if (!strcasecmp(qop, "auth-int") && + stext->requiressf <= 1 && stext->limitssf >= 1) { + oparams->encode = &digestmd5_integrity_encode; + oparams->decode = &digestmd5_integrity_decode; + oparams->mech_ssf = 1; + } else if (!strcasecmp(qop, "auth") && stext->requiressf == 0) { + oparams->encode = NULL; + oparams->decode = NULL; + oparams->mech_ssf = 0; + } else { +#ifdef _SUN_SDK_ + sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR, + "protocol violation: client requested invalid qop"); +#else + SETERROR(sparams->utils, + "protocol violation: client requested invalid qop"); +#endif /* _SUN_SDK_ */ + result = SASL_FAIL; + goto FreeAllMem; + } + + serverresponse = create_response(text, + sparams->utils, + text->nonce, + text->nonce_count, + cnonce, + qop, + digesturi, + A1, + authorization_id, + &text->response_value); + + if (serverresponse == NULL) { +#ifndef _SUN_SDK_ + SETERROR(sparams->utils, "internal error: unable to create response"); +#endif /* !_SUN_SDK_ */ + result = SASL_NOMEM; + goto FreeAllMem; + } + + /* if ok verified */ + if (strcmp(serverresponse, response) != 0) { +#ifdef _INTEGRATED_SOLARIS_ + SETERROR(sparams->utils, + gettext("client response doesn't match what we generated")); +#else + SETERROR(sparams->utils, + "client response doesn't match what we generated"); +#endif /* _INTEGRATED_SOLARIS_ */ + result = SASL_BADAUTH; + + goto FreeAllMem; + } + + /* see if our nonce expired */ + if (text->reauth->timeout && + time(0) - stext->timestamp > text->reauth->timeout) { +#ifdef _INTEGRATED_SOLARIS_ + SETERROR(sparams->utils, gettext("server nonce expired")); +#else + SETERROR(sparams->utils, "server nonce expired"); +#endif /* _INTEGRATED_SOLARIS_ */ + stext->stale = 1; + result = SASL_BADAUTH; + + goto FreeAllMem; + } + + /* + * nothing more to do; authenticated set oparams information + */ + oparams->doneflag = 1; + oparams->maxoutbuf = client_maxbuf - 4; + if (oparams->mech_ssf > 1) { +#ifdef _SUN_SDK_ + if (oparams->maxoutbuf <= 25) { + result = SASL_BADPARAM; + goto FreeAllMem; + } +#endif + /* MAC block (privacy) */ + oparams->maxoutbuf -= 25; + } else if(oparams->mech_ssf == 1) { +#ifdef _SUN_SDK_ + if (oparams->maxoutbuf <= 16) { + result = SASL_BADPARAM; + goto FreeAllMem; + } +#endif + /* MAC block (integrity) */ + oparams->maxoutbuf -= 16; + } + + oparams->param_version = 0; + + text->seqnum = 0; /* for integrity/privacy */ + text->rec_seqnum = 0; /* for integrity/privacy */ + text->in_maxbuf = + sparams->props.maxbufsize ? sparams->props.maxbufsize : DEFAULT_BUFSIZE; + text->utils = sparams->utils; + + /* used by layers */ + text->needsize = 4; + text->buffer = NULL; + + if (oparams->mech_ssf > 0) { + char enckey[16]; + char deckey[16]; + + create_layer_keys(text, sparams->utils,text->HA1,n,enckey,deckey); + + /* initialize cipher if need be */ +#ifdef _SUN_SDK_ + if (text->cipher_init) { + if (text->cipher_free) + text->cipher_free(text); + if ((result = text->cipher_init(text, enckey, deckey)) != SASL_OK) { + sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR, + "couldn't init cipher"); + goto FreeAllMem; + } + } +#else + if (text->cipher_init) + if (text->cipher_init(text, enckey, deckey) != SASL_OK) { + sparams->utils->seterror(sparams->utils->conn, 0, + "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 = + strlen(text->response_value) + strlen("rspauth") + 3; + + result = _plug_buf_alloc(sparams->utils, &(text->out_buf), + &(text->out_buf_len), resplen); + if(result != SASL_OK) { + goto FreeAllMem; + } + + sprintf(text->out_buf, "rspauth=%s", text->response_value); + + /* self check */ + if (strlen(text->out_buf) > 2048) { + result = SASL_FAIL; + goto FreeAllMem; + } + } + + *serveroutlen = strlen(text->out_buf); + *serverout = text->out_buf; + + result = SASL_OK; + + FreeAllMem: + if (text->reauth->timeout && + sparams->utils->mutex_lock(text->reauth->mutex) == SASL_OK) { /* LOCK */ + unsigned val = hash(username) % text->reauth->size; + + switch (result) { + case SASL_OK: + /* successful auth, setup for future reauth */ + if (text->nonce_count == 1) { + /* successful initial auth, create new entry */ + clear_reauth_entry(&text->reauth->e[val], SERVER, sparams->utils); + text->reauth->e[val].authid = username; username = NULL; + text->reauth->e[val].realm = text->realm; text->realm = NULL; + text->reauth->e[val].nonce = text->nonce; text->nonce = NULL; + text->reauth->e[val].cnonce = cnonce; cnonce = NULL; + } + if (text->nonce_count <= text->reauth->e[val].nonce_count) { + /* paranoia. prevent replay attacks */ + clear_reauth_entry(&text->reauth->e[val], SERVER, sparams->utils); + } + else { + text->reauth->e[val].nonce_count = text->nonce_count; + text->reauth->e[val].u.s.timestamp = time(0); + } + break; + default: + if (text->nonce_count > 1) { + /* failed reauth, clear entry */ + clear_reauth_entry(&text->reauth->e[val], SERVER, sparams->utils); + } + else { + /* failed initial auth, leave existing cache */ + } + } + sparams->utils->mutex_unlock(text->reauth->mutex); /* UNLOCK */ + } + + /* free everything */ + if (in_start) sparams->utils->free (in_start); + + if (username != NULL) + sparams->utils->free (username); +#ifdef _SUN_SDK_ + if (authorization_id != NULL) + sparams->utils->free (authorization_id); +#endif /* _SUN_SDK_ */ + if (realm != NULL) + sparams->utils->free (realm); + if (nonce != NULL) + sparams->utils->free (nonce); + if (cnonce != NULL) + sparams->utils->free (cnonce); + if (response != NULL) + sparams->utils->free (response); + if (cipher != NULL) + sparams->utils->free (cipher); + if (serverresponse != NULL) + sparams->utils->free(serverresponse); + if (charset != NULL) + sparams->utils->free (charset); + if (digesturi != NULL) + sparams->utils->free (digesturi); + if (qop!=NULL) + sparams->utils->free (qop); + if (sec) + _plug_free_secret(sparams->utils, &sec); + + return result; +} + +static int +digestmd5_server_mech_step(void *conn_context, + sasl_server_params_t *sparams, + const char *clientin, + unsigned clientinlen, + const char **serverout, + unsigned *serveroutlen, + sasl_out_params_t *oparams) +{ + context_t *text = (context_t *) conn_context; + server_context_t *stext = (server_context_t *) conn_context; + + if (clientinlen > 4096) return SASL_BADPROT; + + *serverout = NULL; + *serveroutlen = 0; + + switch (text->state) { + + case 1: + /* setup SSF limits */ + if (!sparams->props.maxbufsize) { + stext->limitssf = 0; + stext->requiressf = 0; + } else { + if (sparams->props.max_ssf < sparams->external_ssf) { + stext->limitssf = 0; + } else { + stext->limitssf = + sparams->props.max_ssf - sparams->external_ssf; + } + if (sparams->props.min_ssf < sparams->external_ssf) { + stext->requiressf = 0; + } else { + stext->requiressf = + sparams->props.min_ssf - sparams->external_ssf; + } + } + + if (clientin && text->reauth->timeout) { + /* here's where we attempt fast reauth if possible */ + if (digestmd5_server_mech_step2(stext, sparams, + clientin, clientinlen, + serverout, serveroutlen, + oparams) == SASL_OK) { + return SASL_OK; + } + +#ifdef _SUN_SDK_ + sparams->utils->log(sparams->utils->conn, SASL_LOG_WARN, + "DIGEST-MD5 reauth failed"); +#else + sparams->utils->log(NULL, SASL_LOG_WARN, + "DIGEST-MD5 reauth failed\n"); +#endif /* _SUN_SDK_ */ + + /* re-initialize everything for a fresh start */ + memset(oparams, 0, sizeof(sasl_out_params_t)); + + /* fall through and issue challenge */ + } + + return digestmd5_server_mech_step1(stext, sparams, + clientin, clientinlen, + serverout, serveroutlen, oparams); + + case 2: + return digestmd5_server_mech_step2(stext, sparams, + clientin, clientinlen, + serverout, serveroutlen, oparams); + + default: +#ifdef _SUN_SDK_ + sparams->utils->log(sparams->utils->conn, SASL_LOG_ERR, + "Invalid DIGEST-MD5 server step %d", text->state); +#else + sparams->utils->log(NULL, SASL_LOG_ERR, + "Invalid DIGEST-MD5 server step %d\n", text->state); +#endif /* _SUN_SDK_ */ + return SASL_FAIL; + } + +#ifndef _SUN_SDK_ + return SASL_FAIL; /* should never get here */ +#endif /* !_SUN_SDK_ */ +} + +static void +digestmd5_server_mech_dispose(void *conn_context, const sasl_utils_t *utils) +{ + server_context_t *stext = (server_context_t *) conn_context; + + if (!stext || !utils) return; + + digestmd5_common_mech_dispose(conn_context, utils); +} + +static sasl_server_plug_t digestmd5_server_plugins[] = +{ + { + "DIGEST-MD5", /* mech_name */ + /* EXPORT DELETE START */ +#ifdef WITH_RC4 + 128, /* max_ssf */ +#elif WITH_DES + 112, +#else + /* EXPORT DELETE END */ + 0, + /* EXPORT DELETE START */ +#endif + /* EXPORT DELETE END */ + SASL_SEC_NOPLAINTEXT + | SASL_SEC_NOANONYMOUS + | 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 digestmd5_server_plug_init(sasl_utils_t *utils, + int maxversion, + int *out_version, + sasl_server_plug_t **pluglist, + int *plugcount) +{ + reauth_cache_t *reauth_cache; + const char *timeout = NULL; + unsigned int len; +#if defined _SUN_SDK_ && defined USE_UEF + int ret; +#endif /* _SUN_SDK_ && USE_UEF */ + + if (maxversion < SASL_SERVER_PLUG_VERSION) + return SASL_BADVERS; + +#if defined _SUN_SDK_ && defined USE_UEF + if ((ret = uef_init(utils)) != SASL_OK) + return ret; +#endif /* _SUN_SDK_ && USE_UEF */ + + /* reauth cache */ + reauth_cache = utils->malloc(sizeof(reauth_cache_t)); + if (reauth_cache == NULL) + return SASL_NOMEM; + memset(reauth_cache, 0, sizeof(reauth_cache_t)); + reauth_cache->i_am = SERVER; + + /* fetch and canonify the reauth_timeout */ + utils->getopt(utils->getopt_context, "DIGEST-MD5", "reauth_timeout", + &timeout, &len); + if (timeout) + reauth_cache->timeout = (time_t) 60 * strtol(timeout, NULL, 10); +#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 */ + reauth_cache->mutex = utils->mutex_alloc(); + if (!reauth_cache->mutex) + return SASL_FAIL; + + /* entries */ + reauth_cache->size = 100; + reauth_cache->e = utils->malloc(reauth_cache->size * + sizeof(reauth_entry_t)); + if (reauth_cache->e == NULL) + return SASL_NOMEM; + memset(reauth_cache->e, 0, reauth_cache->size * sizeof(reauth_entry_t)); + } + + digestmd5_server_plugins[0].glob_context = reauth_cache; + +#ifdef _SUN_SDK_ +#ifdef USE_UEF_CLIENT + digestmd5_server_plugins[0].max_ssf = uef_max_ssf; +#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. + */ + REG_PLUG("DIGEST-MD5", digestmd5_server_plugins); +#endif /* _INTEGRATED_SOLARIS_ */ + /* CRYPT DELETE END */ + /* EXPORT DELETE END */ + + *out_version = SASL_SERVER_PLUG_VERSION; + *pluglist = digestmd5_server_plugins; + *plugcount = 1; + + return SASL_OK; +} + +/***************************** Client Section *****************************/ + +typedef struct client_context { + context_t common; + + sasl_secret_t *password; /* user password */ + unsigned int free_password; /* set if we need to free password */ + + int protection; + struct digest_cipher *cipher; + unsigned int server_maxbuf; +#ifdef _INTEGRATED_SOLARIS_ + void *h; +#endif /* _INTEGRATED_SOLARIS_ */ +} client_context_t; + +/* calculate H(A1) as per spec */ +static void +DigestCalcHA1(context_t * text, + const sasl_utils_t * utils, + unsigned char *pszUserName, + unsigned char *pszRealm, + sasl_secret_t * pszPassword, + unsigned char *pszAuthorization_id, + unsigned char *pszNonce, + unsigned char *pszCNonce, + HASHHEX SessionKey) +{ + MD5_CTX Md5Ctx; + HASH HA1; + + DigestCalcSecret(utils, + pszUserName, + pszRealm, + (unsigned char *) pszPassword->data, + pszPassword->len, + HA1); + + /* calculate the session key */ + utils->MD5Init(&Md5Ctx); + utils->MD5Update(&Md5Ctx, HA1, HASHLEN); + utils->MD5Update(&Md5Ctx, COLON, 1); + utils->MD5Update(&Md5Ctx, pszNonce, strlen((char *) pszNonce)); + utils->MD5Update(&Md5Ctx, COLON, 1); + utils->MD5Update(&Md5Ctx, pszCNonce, strlen((char *) pszCNonce)); + if (pszAuthorization_id != NULL) { + utils->MD5Update(&Md5Ctx, COLON, 1); + utils->MD5Update(&Md5Ctx, pszAuthorization_id, + strlen((char *) pszAuthorization_id)); + } + utils->MD5Final(HA1, &Md5Ctx); + + CvtHex(HA1, SessionKey); + + /* xxx rc-* use different n */ + + /* save HA1 because we'll need it for the privacy and integrity keys */ + memcpy(text->HA1, HA1, sizeof(HASH)); + +} + +static char *calculate_response(context_t * text, + 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, + sasl_secret_t * passwd, + unsigned char *authorization_id, + char **response_value) +{ + HASHHEX SessionKey; + HASHHEX HEntity = "00000000000000000000000000000000"; + HASHHEX Response; + char *result; + + /* Verifing that all parameters was defined */ + if(!username || !cnonce || !nonce || !ncvalue || !digesturi || !passwd) { + PARAMERROR( utils ); + return NULL; + } + + if (realm == NULL) { + /* a NULL realm is equivalent to the empty string */ + realm = (unsigned char *) ""; + } + + if (qop == NULL) { + /* default to a qop of just authentication */ + qop = "auth"; + } + + DigestCalcHA1(text, + utils, + username, + realm, + passwd, + authorization_id, + nonce, + cnonce, + SessionKey); + + DigestCalcResponse(utils, + 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 */ + ); + + result = utils->malloc(HASHHEXLEN + 1); +#ifdef _SUN_SDK_ + if (result == NULL) + return NULL; +#endif /* _SUN_SDK_ */ + memcpy(result, Response, HASHHEXLEN); + result[HASHHEXLEN] = 0; + + if (response_value != NULL) { + DigestCalcResponse(utils, + 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) + utils->free(*response_value); +#endif /* _SUN_SDK_ */ + *response_value = utils->malloc(HASHHEXLEN + 1); + if (*response_value == NULL) + return NULL; + + memcpy(*response_value, Response, HASHHEXLEN); + (*response_value)[HASHHEXLEN] = 0; + + } + + return result; +} + +static int +make_client_response(context_t *text, + sasl_client_params_t *params, + sasl_out_params_t *oparams) +{ + client_context_t *ctext = (client_context_t *) text; + char *qop = NULL; + unsigned nbits = 0; + unsigned char *digesturi = NULL; + bool IsUTF8 = FALSE; + char ncvalue[10]; + char maxbufstr[64]; + char *response = NULL; + unsigned resplen = 0; + int result; + + switch (ctext->protection) { + case DIGEST_PRIVACY: + qop = "auth-conf"; + oparams->encode = &digestmd5_privacy_encode; + oparams->decode = &digestmd5_privacy_decode; + oparams->mech_ssf = ctext->cipher->ssf; + + nbits = ctext->cipher->n; + text->cipher_enc = ctext->cipher->cipher_enc; + text->cipher_dec = ctext->cipher->cipher_dec; + text->cipher_free = ctext->cipher->cipher_free; + text->cipher_init = ctext->cipher->cipher_init; + break; + case DIGEST_INTEGRITY: + qop = "auth-int"; + oparams->encode = &digestmd5_integrity_encode; + oparams->decode = &digestmd5_integrity_decode; + oparams->mech_ssf = 1; + break; + case DIGEST_NOLAYER: + default: + qop = "auth"; + oparams->encode = NULL; + oparams->decode = NULL; + oparams->mech_ssf = 0; + } + + digesturi = params->utils->malloc(strlen(params->service) + 1 + + strlen(params->serverFQDN) + 1 + + 1); + if (digesturi == NULL) { + result = SASL_NOMEM; + goto FreeAllocatedMem; + }; + + /* allocated exactly this. safe */ + strcpy((char *) digesturi, params->service); + strcat((char *) digesturi, "/"); + strcat((char *) digesturi, params->serverFQDN); + /* + * strcat (digesturi, "/"); strcat (digesturi, params->serverFQDN); + */ + + /* response */ + response = + calculate_response(text, + params->utils, +#ifdef _SUN_SDK_ + (unsigned char *) oparams->authid, +#else + (char *) oparams->authid, +#endif /* _SUN_SDK_ */ + (unsigned char *) text->realm, + text->nonce, + text->nonce_count, + text->cnonce, + qop, + digesturi, + ctext->password, + strcmp(oparams->user, oparams->authid) ? +#ifdef _SUN_SDK_ + (unsigned char *) oparams->user : NULL, +#else + (char *) oparams->user : NULL, +#endif /* _SUN_SDK_ */ + &text->response_value); + +#ifdef _SUN_SDK_ + if (response == NULL) { + result = SASL_NOMEM; + goto FreeAllocatedMem; + } +#endif /* _SUN_SDK_ */ + + resplen = strlen(oparams->authid) + strlen("username") + 5; + result =_plug_buf_alloc(params->utils, &(text->out_buf), + &(text->out_buf_len), + resplen); + if (result != SASL_OK) goto FreeAllocatedMem; + + sprintf(text->out_buf, "username=\"%s\"", oparams->authid); + + if (add_to_challenge(params->utils, + &text->out_buf, &text->out_buf_len, &resplen, + "realm", (unsigned char *) text->realm, + TRUE) != SASL_OK) { + result = SASL_FAIL; + goto FreeAllocatedMem; + } + if (strcmp(oparams->user, oparams->authid)) { + if (add_to_challenge(params->utils, + &text->out_buf, &text->out_buf_len, &resplen, +#ifdef _SUN_SDK_ + "authzid", (unsigned char *) oparams->user, + TRUE) != SASL_OK) { +#else + "authzid", (char *) oparams->user, TRUE) != SASL_OK) { +#endif /* _SUN_SDK_ */ + result = SASL_FAIL; + goto FreeAllocatedMem; + } + } + if (add_to_challenge(params->utils, + &text->out_buf, &text->out_buf_len, &resplen, + "nonce", text->nonce, TRUE) != SASL_OK) { + result = SASL_FAIL; + goto FreeAllocatedMem; + } + if (add_to_challenge(params->utils, + &text->out_buf, &text->out_buf_len, &resplen, + "cnonce", text->cnonce, TRUE) != SASL_OK) { + result = SASL_FAIL; + goto FreeAllocatedMem; + } + snprintf(ncvalue, sizeof(ncvalue), "%08x", text->nonce_count); + if (add_to_challenge(params->utils, + &text->out_buf, &text->out_buf_len, &resplen, + "nc", (unsigned char *) ncvalue, FALSE) != SASL_OK) { + result = SASL_FAIL; + goto FreeAllocatedMem; + } + if (add_to_challenge(params->utils, + &text->out_buf, &text->out_buf_len, &resplen, + "qop", (unsigned char *) qop, FALSE) != SASL_OK) { + result = SASL_FAIL; + goto FreeAllocatedMem; + } + if (ctext->cipher != NULL) { + if (add_to_challenge(params->utils, + &text->out_buf, &text->out_buf_len, &resplen, + "cipher", + (unsigned char *) ctext->cipher->name, + TRUE) != SASL_OK) { + result = SASL_FAIL; + goto FreeAllocatedMem; + } + } + + if (params->props.maxbufsize) { + snprintf(maxbufstr, sizeof(maxbufstr), "%d", params->props.maxbufsize); + if (add_to_challenge(params->utils, + &text->out_buf, &text->out_buf_len, &resplen, + "maxbuf", (unsigned char *) maxbufstr, + FALSE) != SASL_OK) { +#ifdef _SUN_SDK_ + params->utils->log(params->utils->conn, SASL_LOG_ERR, + "internal error: add_to_challenge maxbuf failed"); +#else + SETERROR(params->utils, + "internal error: add_to_challenge maxbuf failed"); +#endif /* _SUN_SDK_ */ + goto FreeAllocatedMem; + } + } + + if (IsUTF8) { + if (add_to_challenge(params->utils, + &text->out_buf, &text->out_buf_len, &resplen, + "charset", (unsigned char *) "utf-8", + FALSE) != SASL_OK) { + result = SASL_FAIL; + goto FreeAllocatedMem; + } + } + if (add_to_challenge(params->utils, + &text->out_buf, &text->out_buf_len, &resplen, + "digest-uri", digesturi, TRUE) != SASL_OK) { + result = SASL_FAIL; + goto FreeAllocatedMem; + } + if (add_to_challenge(params->utils, + &text->out_buf, &text->out_buf_len, &resplen, + "response", (unsigned char *) response, + FALSE) != SASL_OK) { + + result = SASL_FAIL; + goto FreeAllocatedMem; + } + + /* self check */ + if (strlen(text->out_buf) > 2048) { + result = SASL_FAIL; + goto FreeAllocatedMem; + } + + /* set oparams */ +#ifdef _SUN_SDK_ + oparams->maxoutbuf = ctext->server_maxbuf - 4; +#else + oparams->maxoutbuf = ctext->server_maxbuf; +#endif /* _SUN_SDK_ */ + if(oparams->mech_ssf > 1) { +#ifdef _SUN_SDK_ + if (oparams->maxoutbuf <= 25) + return (SASL_BADPARAM); +#endif + /* MAC block (privacy) */ + oparams->maxoutbuf -= 25; + } else if(oparams->mech_ssf == 1) { +#ifdef _SUN_SDK_ + if (oparams->maxoutbuf <= 16) + return (SASL_BADPARAM); +#endif + /* MAC block (integrity) */ + oparams->maxoutbuf -= 16; + } + + text->seqnum = 0; /* for integrity/privacy */ + text->rec_seqnum = 0; /* for integrity/privacy */ + text->utils = params->utils; + + text->in_maxbuf = + params->props.maxbufsize ? params->props.maxbufsize : DEFAULT_BUFSIZE; + + /* used by layers */ + text->needsize = 4; + text->buffer = NULL; + + if (oparams->mech_ssf > 0) { + char enckey[16]; + char deckey[16]; + + create_layer_keys(text, params->utils, text->HA1, nbits, + enckey, deckey); + + /* initialize cipher if need be */ +#ifdef _SUN_SDK_ + if (text->cipher_init) { + if (text->cipher_free) + text->cipher_free(text); + if((result = text->cipher_init(text, enckey, deckey)) != SASL_OK) { + params->utils->log(params->utils->conn, SASL_LOG_ERR, + "couldn't init cipher"); + goto FreeAllocatedMem; + } + } +#else + if (text->cipher_init) + text->cipher_init(text, enckey, deckey); +#endif /* _SUN_SDK_ */ + } + + result = SASL_OK; + + FreeAllocatedMem: + if (digesturi) params->utils->free(digesturi); + if (response) params->utils->free(response); + + return result; +} + +static int parse_server_challenge(client_context_t *ctext, + sasl_client_params_t *params, + const char *serverin, unsigned serverinlen, + char ***outrealms, int *noutrealm) +{ + context_t *text = (context_t *) ctext; + int result = SASL_OK; + char *in_start = NULL; + char *in = NULL; + char **realms = NULL; + int nrealm = 0; + sasl_ssf_t limit, musthave = 0; + sasl_ssf_t external; + int protection = 0; + int ciphers = 0; + int maxbuf_count = 0; +#ifndef _SUN_SDK_ + bool IsUTF8 = FALSE; +#endif /* !_SUN_SDK_ */ + int algorithm_count = 0; + + if (!serverin || !serverinlen) { +#ifndef _SUN_SDK_ + params->utils->log(params->utils->conn, SASL_LOG_ERR, + "no server challenge"); +#else + params->utils->seterror(params->utils->conn, 0, + "no server challenge"); +#endif /* _SUN_SDK_ */ + return SASL_FAIL; + } + + in_start = in = params->utils->malloc(serverinlen + 1); + if (in == NULL) return SASL_NOMEM; + + memcpy(in, serverin, serverinlen); + in[serverinlen] = 0; + + ctext->server_maxbuf = 65536; /* Default value for maxbuf */ + + /* create a new cnonce */ + text->cnonce = create_nonce(params->utils); + if (text->cnonce == NULL) { +#ifdef _SUN_SDK_ + params->utils->log(params->utils->conn, SASL_LOG_ERR, + "failed to create cnonce"); +#else + params->utils->seterror(params->utils->conn, 0, + "failed to create cnonce"); +#endif /* _SUN_SDK_ */ + result = SASL_FAIL; + goto FreeAllocatedMem; + } + + /* parse the challenge */ + while (in[0] != '\0') { + char *name, *value; + + get_pair(&in, &name, &value); + + /* if parse error */ + if (name == NULL) { +#ifdef _SUN_SDK_ + params->utils->log(params->utils->conn, SASL_LOG_ERR, + "Parse error"); +#else + params->utils->seterror(params->utils->conn, 0, "Parse error"); +#endif /* _SUN_SDK_ */ + result = SASL_FAIL; + goto FreeAllocatedMem; + } + + if (strcasecmp(name, "realm") == 0) { + nrealm++; + + if(!realms) + realms = params->utils->malloc(sizeof(char *) * (nrealm + 1)); + else + realms = params->utils->realloc(realms, + sizeof(char *) * (nrealm + 1)); + + if (realms == NULL) { + result = SASL_NOMEM; + goto FreeAllocatedMem; + } + + _plug_strdup(params->utils, value, &realms[nrealm-1], NULL); + realms[nrealm] = NULL; + } else if (strcasecmp(name, "nonce") == 0) { + _plug_strdup(params->utils, value, (char **) &text->nonce, + NULL); + text->nonce_count = 1; + } else if (strcasecmp(name, "qop") == 0) { + while (value && *value) { + char *comma = strchr(value, ','); + if (comma != NULL) { + *comma++ = '\0'; + } + + if (strcasecmp(value, "auth-conf") == 0) { + protection |= DIGEST_PRIVACY; + } else if (strcasecmp(value, "auth-int") == 0) { + protection |= DIGEST_INTEGRITY; + } else if (strcasecmp(value, "auth") == 0) { + protection |= DIGEST_NOLAYER; + } else { + params->utils->log(params->utils->conn, SASL_LOG_DEBUG, + "Server supports unknown layer: %s\n", + value); + } + + value = comma; + } + + if (protection == 0) { + result = SASL_BADAUTH; +#ifdef _INTEGRATED_SOLARIS_ + params->utils->seterror(params->utils->conn, 0, + gettext("Server doesn't support known qop level")); +#else + params->utils->seterror(params->utils->conn, 0, + "Server doesn't support known qop level"); +#endif /* _INTEGRATED_SOLARIS_ */ + goto FreeAllocatedMem; + } + } else if (strcasecmp(name, "cipher") == 0) { + while (value && *value) { + char *comma = strchr(value, ','); +#ifdef USE_UEF_CLIENT + struct digest_cipher *cipher = available_ciphers1; +#else + struct digest_cipher *cipher = available_ciphers; +#endif + + if (comma != NULL) { + *comma++ = '\0'; + } + + /* do we support this cipher? */ + while (cipher->name) { + if (!strcasecmp(value, cipher->name)) break; + cipher++; + } + if (cipher->name) { + ciphers |= cipher->flag; + } else { + params->utils->log(params->utils->conn, SASL_LOG_DEBUG, + "Server supports unknown cipher: %s\n", + value); + } + + value = comma; + } + } else if (strcasecmp(name, "stale") == 0 && ctext->password) { + /* clear any cached password */ + if (ctext->free_password) + _plug_free_secret(params->utils, &ctext->password); + ctext->password = NULL; + } else if (strcasecmp(name, "maxbuf") == 0) { + /* 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) { + result = SASL_BADAUTH; +#ifdef _SUN_SDK_ + params->utils->log(params->utils->conn, SASL_LOG_ERR, + "At least two maxbuf directives found." + " Authentication aborted"); +#else + params->utils->seterror(params->utils->conn, 0, + "At least two maxbuf directives found. Authentication aborted"); +#endif /* _SUN_SDK_ */ + goto FreeAllocatedMem; + } else if (sscanf(value, "%u", &ctext->server_maxbuf) != 1) { + result = SASL_BADAUTH; +#ifdef _SUN_SDK_ + params->utils->log(params->utils->conn, SASL_LOG_ERR, + "Invalid maxbuf parameter received from server"); +#else + params->utils->seterror(params->utils->conn, 0, + "Invalid maxbuf parameter received from server"); +#endif /* _SUN_SDK_ */ + goto FreeAllocatedMem; + } else { + if (ctext->server_maxbuf<=16) { + result = SASL_BADAUTH; +#ifdef _SUN_SDK_ + params->utils->log(params->utils->conn, SASL_LOG_ERR, + "Invalid maxbuf parameter received from server" + " (too small: %s)", value); +#else + params->utils->seterror(params->utils->conn, 0, + "Invalid maxbuf parameter received from server (too small: %s)", value); +#endif /* _SUN_SDK_ */ + goto FreeAllocatedMem; + } + } + } else if (strcasecmp(name, "charset") == 0) { + if (strcasecmp(value, "utf-8") != 0) { + result = SASL_BADAUTH; +#ifdef _SUN_SDK_ + params->utils->log(params->utils->conn, SASL_LOG_ERR, + "Charset must be UTF-8"); +#else + params->utils->seterror(params->utils->conn, 0, + "Charset must be UTF-8"); +#endif /* _SUN_SDK_ */ + goto FreeAllocatedMem; + } else { +#ifndef _SUN_SDK_ + IsUTF8 = TRUE; +#endif /* !_SUN_SDK_ */ + } + } else if (strcasecmp(name,"algorithm")==0) { + if (strcasecmp(value, "md5-sess") != 0) + { +#ifdef _SUN_SDK_ + params->utils->log(params->utils->conn, SASL_LOG_ERR, + "'algorithm' isn't 'md5-sess'"); +#else + params->utils->seterror(params->utils->conn, 0, + "'algorithm' isn't 'md5-sess'"); +#endif /* _SUN_SDK_ */ + result = SASL_FAIL; + goto FreeAllocatedMem; + } + + algorithm_count++; + if (algorithm_count > 1) + { +#ifdef _SUN_SDK_ + params->utils->log(params->utils->conn, SASL_LOG_ERR, + "Must see 'algorithm' only once"); +#else + params->utils->seterror(params->utils->conn, 0, + "Must see 'algorithm' only once"); +#endif /* _SUN_SDK_ */ + result = SASL_FAIL; + goto FreeAllocatedMem; + } + } else { + params->utils->log(params->utils->conn, SASL_LOG_DEBUG, + "DIGEST-MD5 unrecognized pair %s/%s: ignoring", + name, value); + } + } + + if (algorithm_count != 1) { +#ifdef _SUN_SDK_ + params->utils->log(params->utils->conn, SASL_LOG_ERR, + "Must see 'algorithm' once. Didn't see at all"); +#else + params->utils->seterror(params->utils->conn, 0, + "Must see 'algorithm' once. Didn't see at all"); +#endif /* _SUN_SDK_ */ + result = SASL_FAIL; + goto FreeAllocatedMem; + } + + /* make sure we have everything we require */ + if (text->nonce == NULL) { +#ifdef _SUN_SDK_ + params->utils->log(params->utils->conn, SASL_LOG_ERR, + "Don't have nonce."); +#else + params->utils->seterror(params->utils->conn, 0, + "Don't have nonce."); +#endif /* _SUN_SDK_ */ + result = SASL_FAIL; + goto FreeAllocatedMem; + } + + /* get requested ssf */ + external = params->external_ssf; + + /* what do we _need_? how much is too much? */ + if (params->props.maxbufsize == 0) { + musthave = 0; + limit = 0; + } else { + if (params->props.max_ssf > external) { + limit = params->props.max_ssf - external; + } else { + limit = 0; + } + if (params->props.min_ssf > external) { + musthave = params->props.min_ssf - external; + } else { + musthave = 0; + } + } + + /* we now go searching for an option that gives us at least "musthave" + and at most "limit" bits of ssf. */ + if ((limit > 1) && (protection & DIGEST_PRIVACY)) { + struct digest_cipher *cipher; + + /* let's find an encryption scheme that we like */ +#ifdef USE_UEF_CLIENT + cipher = available_ciphers1; +#else + cipher = available_ciphers; +#endif + while (cipher->name) { + /* 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 */ + if ((limit >= cipher->ssf) && (musthave <= cipher->ssf) && + (ciphers & cipher->flag) && + (!ctext->cipher || (cipher->ssf > ctext->cipher->ssf))) { + ctext->cipher = cipher; + } + cipher++; + } + + if (ctext->cipher) { + /* we found a cipher we like */ + ctext->protection = DIGEST_PRIVACY; + } else { + /* we didn't find any ciphers we like */ +#ifdef _INTEGRATED_SOLARIS_ + params->utils->seterror(params->utils->conn, 0, + gettext("No good privacy layers")); +#else + params->utils->seterror(params->utils->conn, 0, + "No good privacy layers"); +#endif /* _INTEGRATED_SOLARIS_ */ + } + } + + if (ctext->cipher == NULL) { + /* we failed to find an encryption layer we liked; + can we use integrity or nothing? */ + + if ((limit >= 1) && (musthave <= 1) + && (protection & DIGEST_INTEGRITY)) { + /* integrity */ + ctext->protection = DIGEST_INTEGRITY; +#ifdef _SUN_SDK_ + } else if (musthave == 0) { +#else + } else if (musthave <= 0) { +#endif /* _SUN_SDK_ */ + /* no layer */ + ctext->protection = DIGEST_NOLAYER; + + /* See if server supports not having a layer */ + if ((protection & DIGEST_NOLAYER) != DIGEST_NOLAYER) { +#ifdef _INTEGRATED_SOLARIS_ + params->utils->seterror(params->utils->conn, 0, + gettext("Server doesn't support \"no layer\"")); +#else + params->utils->seterror(params->utils->conn, 0, + "Server doesn't support \"no layer\""); +#endif /* _INTEGRATED_SOLARIS_ */ + result = SASL_FAIL; + goto FreeAllocatedMem; + } + } else { +#ifdef _INTEGRATED_SOLARIS_ + params->utils->seterror(params->utils->conn, 0, + gettext("Can't find an acceptable layer")); +#else + params->utils->seterror(params->utils->conn, 0, + "Can't find an acceptable layer"); +#endif /* _INTEGRATED_SOLARIS_ */ + result = SASL_TOOWEAK; + goto FreeAllocatedMem; + } + } + + *outrealms = realms; + *noutrealm = nrealm; + + FreeAllocatedMem: + if (in_start) params->utils->free(in_start); + + if (result != SASL_OK && realms) { + int lup; + + /* need to free all the realms */ + for (lup = 0;lup < nrealm; lup++) + params->utils->free(realms[lup]); + + params->utils->free(realms); + } + + return result; +} + +static int ask_user_info(client_context_t *ctext, + sasl_client_params_t *params, + char **realms, int nrealm, + sasl_interact_t **prompt_need, + sasl_out_params_t *oparams) +{ + context_t *text = (context_t *) ctext; + int result = SASL_OK; + const char *authid = NULL, *userid = NULL, *realm = NULL; + char *realm_chal = NULL; + int user_result = SASL_OK; + int auth_result = SASL_OK; + int pass_result = SASL_OK; + int realm_result = SASL_FAIL; + + /* try to get the authid */ + if (oparams->authid == NULL) { + auth_result = _plug_get_authid(params->utils, &authid, prompt_need); + + if ((auth_result != SASL_OK) && (auth_result != SASL_INTERACT)) { + return auth_result; + } + } + + /* try to get the userid */ + if (oparams->user == NULL) { + user_result = _plug_get_userid(params->utils, &userid, prompt_need); + + if ((user_result != SASL_OK) && (user_result != SASL_INTERACT)) { + return user_result; + } + } + + /* try to get the password */ + if (ctext->password == NULL) { + pass_result = _plug_get_password(params->utils, &ctext->password, + &ctext->free_password, prompt_need); + if ((pass_result != SASL_OK) && (pass_result != SASL_INTERACT)) { + return pass_result; + } + } + + /* try to get the realm */ + if (text->realm == NULL) { + if (realms) { + if(nrealm == 1) { + /* only one choice */ + realm = realms[0]; + realm_result = SASL_OK; + } else { + /* ask the user */ + realm_result = _plug_get_realm(params->utils, + (const char **) realms, + (const char **) &realm, + prompt_need); + } + } + + /* fake the realm if we must */ + if ((realm_result != SASL_OK) && (realm_result != SASL_INTERACT)) { + if (params->serverFQDN) { + realm = params->serverFQDN; + } else { + return realm_result; + } + } + } + + /* free prompts we got */ + if (prompt_need && *prompt_need) { + params->utils->free(*prompt_need); + *prompt_need = NULL; + } + + /* if there are prompts not filled in */ + if ((user_result == SASL_INTERACT) || (auth_result == SASL_INTERACT) || + (pass_result == SASL_INTERACT) || (realm_result == SASL_INTERACT)) { + + /* make our default realm */ + if ((realm_result == SASL_INTERACT) && params->serverFQDN) { + realm_chal = params->utils->malloc(3+strlen(params->serverFQDN)); + if (realm_chal) { + sprintf(realm_chal, "{%s}", params->serverFQDN); + } else { + return SASL_NOMEM; + } + } + + /* make the prompt list */ + result = +#if defined _INTEGRATED_SOLARIS_ + _plug_make_prompts(params->utils, &ctext->h, prompt_need, + user_result == SASL_INTERACT ? + convert_prompt(params->utils, &ctext->h, + gettext("Please enter your authorization name")) + : NULL, + NULL, + auth_result == SASL_INTERACT ? + convert_prompt(params->utils, &ctext->h, + gettext("Please enter your authentication name")) + : NULL, + NULL, + pass_result == SASL_INTERACT ? + convert_prompt(params->utils, &ctext->h, + gettext("Please enter your password")) + : NULL, NULL, + NULL, NULL, NULL, + realm_chal ? realm_chal : "{}", + realm_result == SASL_INTERACT ? + convert_prompt(params->utils, &ctext->h, + gettext("Please enter your realm")) : NULL, + params->serverFQDN ? params->serverFQDN : NULL); +#else + _plug_make_prompts(params->utils, prompt_need, + 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 password" : NULL, NULL, + NULL, NULL, NULL, + realm_chal ? realm_chal : "{}", + realm_result == SASL_INTERACT ? + "Please enter your realm" : NULL, + params->serverFQDN ? params->serverFQDN : NULL); +#endif /* _INTEGRATED_SOLARIS_ */ + + if (result == SASL_OK) return SASL_INTERACT; + + return result; + } + + if (oparams->authid == NULL) { + if (!userid || !*userid) { + result = params->canon_user(params->utils->conn, authid, 0, + SASL_CU_AUTHID | SASL_CU_AUTHZID, + oparams); + } + else { + result = params->canon_user(params->utils->conn, + authid, 0, SASL_CU_AUTHID, oparams); + if (result != SASL_OK) return result; + + result = params->canon_user(params->utils->conn, + userid, 0, SASL_CU_AUTHZID, oparams); + } + if (result != SASL_OK) return result; + } + + /* Get an allocated version of the realm into the structure */ + if (realm && text->realm == NULL) { + _plug_strdup(params->utils, realm, (char **) &text->realm, NULL); + } + + return result; +} + +static int +digestmd5_client_mech_new(void *glob_context, + sasl_client_params_t * params, + void **conn_context) +{ + context_t *text; + + /* holds state are in -- allocate client size */ + text = params->utils->malloc(sizeof(client_context_t)); + if (text == NULL) + return SASL_NOMEM; + memset(text, 0, sizeof(client_context_t)); + + text->state = 1; + text->i_am = CLIENT; + text->reauth = glob_context; + + *conn_context = text; + + return SASL_OK; +} + +static int +digestmd5_client_mech_step1(client_context_t *ctext, + sasl_client_params_t *params, + const char *serverin __attribute__((unused)), + unsigned serverinlen __attribute__((unused)), + sasl_interact_t **prompt_need, + const char **clientout, + unsigned *clientoutlen, + sasl_out_params_t *oparams) +{ + context_t *text = (context_t *) ctext; + int result = SASL_FAIL; + unsigned val; + + params->utils->log(params->utils->conn, SASL_LOG_DEBUG, + "DIGEST-MD5 client step 1"); + + result = ask_user_info(ctext, params, NULL, 0, prompt_need, oparams); + if (result != SASL_OK) return result; + + /* check if we have cached info for this user on this server */ + val = hash(params->serverFQDN) % text->reauth->size; + if (params->utils->mutex_lock(text->reauth->mutex) == SASL_OK) { /* LOCK */ + if (text->reauth->e[val].u.c.serverFQDN && + !strcasecmp(text->reauth->e[val].u.c.serverFQDN, + params->serverFQDN) && + !strcmp(text->reauth->e[val].authid, oparams->authid)) { + +#ifdef _SUN_SDK_ + if (text->realm) params->utils->free(text->realm); + if (text->nonce) params->utils->free(text->nonce); + if (text->cnonce) params->utils->free(text->cnonce); +#endif /* _SUN_SDK_ */ + /* we have info, so use it */ + _plug_strdup(params->utils, text->reauth->e[val].realm, + &text->realm, NULL); +#ifdef _SUN_SDK_ + _plug_strdup(params->utils, (char *)text->reauth->e[val].nonce, + (char **) &text->nonce, NULL); +#else + _plug_strdup(params->utils, text->reauth->e[val].nonce, + (char **) &text->nonce, NULL); +#endif /* _SUN_SDK_ */ + text->nonce_count = ++text->reauth->e[val].nonce_count; +#ifdef _SUN_SDK_ + _plug_strdup(params->utils, (char *)text->reauth->e[val].cnonce, + (char **) &text->cnonce, NULL); +#else + _plug_strdup(params->utils, text->reauth->e[val].cnonce, + (char **) &text->cnonce, NULL); +#endif /* _SUN_SDK_ */ + ctext->protection = text->reauth->e[val].u.c.protection; + ctext->cipher = text->reauth->e[val].u.c.cipher; + ctext->server_maxbuf = text->reauth->e[val].u.c.server_maxbuf; + } + params->utils->mutex_unlock(text->reauth->mutex); /* UNLOCK */ + } + + if (!text->nonce) { + /* we don't have any reauth info, so just return + * that there is no initial client send */ + text->state = 2; + return SASL_CONTINUE; + } + + /* + * (username | realm | nonce | cnonce | nonce-count | qop digest-uri | + * response | maxbuf | charset | auth-param ) + */ + + result = make_client_response(text, params, oparams); + if (result != SASL_OK) return result; + + *clientoutlen = strlen(text->out_buf); + *clientout = text->out_buf; + + text->state = 3; + return SASL_CONTINUE; +} + +static int +digestmd5_client_mech_step2(client_context_t *ctext, + sasl_client_params_t *params, + const char *serverin, + unsigned serverinlen, + sasl_interact_t **prompt_need, + const char **clientout, + unsigned *clientoutlen, + sasl_out_params_t *oparams) +{ + context_t *text = (context_t *) ctext; + int result = SASL_FAIL; + char **realms = NULL; + int nrealm = 0; + + params->utils->log(params->utils->conn, SASL_LOG_DEBUG, + "DIGEST-MD5 client step 2"); + + if (params->props.min_ssf > params->props.max_ssf) { + return SASL_BADPARAM; + } + + /* don't bother parsing the challenge more than once */ + if (text->nonce == NULL) { + result = parse_server_challenge(ctext, params, serverin, serverinlen, + &realms, &nrealm); + if (result != SASL_OK) goto FreeAllocatedMem; + + if (nrealm == 1) { + /* only one choice! */ + text->realm = realms[0]; + + /* free realms */ + params->utils->free(realms); + realms = NULL; + } + } + + result = ask_user_info(ctext, params, realms, nrealm, + prompt_need, oparams); + if (result != SASL_OK) goto FreeAllocatedMem; + + /* + * (username | realm | nonce | cnonce | nonce-count | qop digest-uri | + * response | maxbuf | charset | auth-param ) + */ + + result = make_client_response(text, params, oparams); + if (result != SASL_OK) goto FreeAllocatedMem; + + *clientoutlen = strlen(text->out_buf); + *clientout = text->out_buf; + + text->state = 3; + + result = SASL_CONTINUE; + + FreeAllocatedMem: + if (realms) { + int lup; + + /* need to free all the realms */ + for (lup = 0;lup < nrealm; lup++) + params->utils->free(realms[lup]); + + params->utils->free(realms); + } + + return result; +} + +static int +digestmd5_client_mech_step3(client_context_t *ctext, + sasl_client_params_t *params, + const char *serverin, + unsigned serverinlen, + sasl_interact_t **prompt_need __attribute__((unused)), + const char **clientout __attribute__((unused)), + unsigned *clientoutlen __attribute__((unused)), + sasl_out_params_t *oparams) +{ + context_t *text = (context_t *) ctext; + char *in = NULL; + char *in_start; + int result = SASL_FAIL; + + params->utils->log(params->utils->conn, SASL_LOG_DEBUG, + "DIGEST-MD5 client step 3"); + + /* Verify that server is really what he claims to be */ + in_start = in = params->utils->malloc(serverinlen + 1); + if (in == NULL) return SASL_NOMEM; + + memcpy(in, serverin, serverinlen); + in[serverinlen] = 0; + + /* parse the response */ + while (in[0] != '\0') { + char *name, *value; + get_pair(&in, &name, &value); + + if (name == NULL) { +#ifdef _SUN_SDK_ + params->utils->log(params->utils->conn, SASL_LOG_ERR, + "DIGEST-MD5 Received Garbage"); +#else + params->utils->seterror(params->utils->conn, 0, + "DIGEST-MD5 Received Garbage"); +#endif /* _SUN_SDK_ */ + break; + } + + if (strcasecmp(name, "rspauth") == 0) { + + if (strcmp(text->response_value, value) != 0) { +#ifdef _INTEGRATED_SOLARIS_ + params->utils->seterror(params->utils->conn, 0, + gettext("Server authentication failed")); +#else + params->utils->seterror(params->utils->conn, 0, + "DIGEST-MD5: This server wants us to believe that he knows shared secret"); +#endif /* _INTEGRATED_SOLARIS_ */ + result = SASL_FAIL; + } else { + oparams->doneflag = 1; + oparams->param_version = 0; + + result = SASL_OK; + } + break; + } else { + params->utils->log(params->utils->conn, SASL_LOG_DEBUG, + "DIGEST-MD5 unrecognized pair %s/%s: ignoring", + name, value); + } + } + + params->utils->free(in_start); + + if (params->utils->mutex_lock(text->reauth->mutex) == SASL_OK) { /* LOCK */ + unsigned val = hash(params->serverFQDN) % text->reauth->size; + switch (result) { + case SASL_OK: + if (text->nonce_count == 1) { + /* successful initial auth, setup for future reauth */ + clear_reauth_entry(&text->reauth->e[val], CLIENT, params->utils); + _plug_strdup(params->utils, oparams->authid, + &text->reauth->e[val].authid, NULL); + text->reauth->e[val].realm = text->realm; text->realm = NULL; + text->reauth->e[val].nonce = text->nonce; text->nonce = NULL; + text->reauth->e[val].nonce_count = text->nonce_count; + text->reauth->e[val].cnonce = text->cnonce; text->cnonce = NULL; + _plug_strdup(params->utils, params->serverFQDN, + &text->reauth->e[val].u.c.serverFQDN, NULL); + text->reauth->e[val].u.c.protection = ctext->protection; + text->reauth->e[val].u.c.cipher = ctext->cipher; + text->reauth->e[val].u.c.server_maxbuf = ctext->server_maxbuf; + } +#ifndef _SUN_SDK_ + else { + /* reauth, we already incremented nonce_count */ + } +#endif /* !_SUN_SDK_ */ + break; + default: + if (text->nonce_count > 1) { + /* failed reauth, clear cache */ + clear_reauth_entry(&text->reauth->e[val], CLIENT, params->utils); + } + else { + /* failed initial auth, leave existing cache */ + } + } + params->utils->mutex_unlock(text->reauth->mutex); /* UNLOCK */ + } + + return result; +} + +static int +digestmd5_client_mech_step(void *conn_context, + sasl_client_params_t *params, + const char *serverin, + unsigned serverinlen, + sasl_interact_t **prompt_need, + const char **clientout, + unsigned *clientoutlen, + sasl_out_params_t *oparams) +{ + context_t *text = (context_t *) conn_context; + client_context_t *ctext = (client_context_t *) conn_context; + unsigned val = hash(params->serverFQDN) % text->reauth->size; + + if (serverinlen > 2048) return SASL_BADPROT; + + *clientout = NULL; + *clientoutlen = 0; + + switch (text->state) { + + 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 */ + if (params->utils->mutex_lock(text->reauth->mutex) == SASL_OK) { /* LOCK */ + reauth = text->reauth->e[val].u.c.serverFQDN && + !strcasecmp(text->reauth->e[val].u.c.serverFQDN, + params->serverFQDN); + params->utils->mutex_unlock(text->reauth->mutex); /* UNLOCK */ + } + if (reauth) { + return digestmd5_client_mech_step1(ctext, params, + serverin, serverinlen, + prompt_need, + clientout, clientoutlen, + oparams); + } + else { + /* we don't have any reauth info, so just return + * that there is no initial client send */ + text->state = 2; + return SASL_CONTINUE; + } + } + + /* fall through and respond to challenge */ + + case 3: + if (serverin && !strncasecmp(serverin, "rspauth=", 8)) { + return digestmd5_client_mech_step3(ctext, params, + serverin, serverinlen, + prompt_need, + clientout, clientoutlen, + oparams); + } + + /* fall through and respond to challenge */ + text->state = 2; + + /* cleanup after a failed reauth attempt */ + if (params->utils->mutex_lock(text->reauth->mutex) == SASL_OK) { /* LOCK */ + clear_reauth_entry(&text->reauth->e[val], CLIENT, params->utils); + + params->utils->mutex_unlock(text->reauth->mutex); /* UNLOCK */ + } + + if (text->realm) params->utils->free(text->realm); + if (text->nonce) params->utils->free(text->nonce); + if (text->cnonce) params->utils->free(text->cnonce); +#ifdef _SUN_SDK_ + text->realm = NULL; + text->nonce = text->cnonce = NULL; +#else + text->realm = text->nonce = text->cnonce = NULL; +#endif /* _SUN_SDK_ */ + ctext->cipher = NULL; + + case 2: + return digestmd5_client_mech_step2(ctext, params, + serverin, serverinlen, + prompt_need, + clientout, clientoutlen, + oparams); + + default: +#ifdef _SUN_SDK_ + params->utils->log(params->utils->conn, SASL_LOG_ERR, + "Invalid DIGEST-MD5 client step %d", text->state); +#else + params->utils->log(NULL, SASL_LOG_ERR, + "Invalid DIGEST-MD5 client step %d\n", text->state); +#endif /* _SUN_SDK_ */ + return SASL_FAIL; + } + + return SASL_FAIL; /* should never get here */ +} + +static void +digestmd5_client_mech_dispose(void *conn_context, const sasl_utils_t *utils) +{ + client_context_t *ctext = (client_context_t *) conn_context; + + if (!ctext || !utils) return; + +#ifdef _INTEGRATED_SOLARIS_ + convert_prompt(utils, &ctext->h, NULL); +#endif /* _INTEGRATED_SOLARIS_ */ + + if (ctext->free_password) _plug_free_secret(utils, &ctext->password); + + digestmd5_common_mech_dispose(conn_context, utils); +} + +static sasl_client_plug_t digestmd5_client_plugins[] = +{ + { + "DIGEST-MD5", + /* EXPORT DELETE START */ +#ifdef WITH_RC4 /* mech_name */ + 128, /* max ssf */ +#elif WITH_DES + 112, +#else + /* EXPORT DELETE END */ + 0, + /* EXPORT DELETE START */ +#endif + /* EXPORT DELETE END */ + SASL_SEC_NOPLAINTEXT + | SASL_SEC_NOANONYMOUS + | 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 digestmd5_client_plug_init(sasl_utils_t *utils, + int maxversion, + int *out_version, + sasl_client_plug_t **pluglist, + int *plugcount) +{ + reauth_cache_t *reauth_cache; +#if defined _SUN_SDK_ && defined USE_UEF + int ret; +#endif /* _SUN_SDK_ && USE_UEF */ + + if (maxversion < SASL_CLIENT_PLUG_VERSION) + return SASL_BADVERS; + +#if defined _SUN_SDK_ && defined USE_UEF + if ((ret = uef_init(utils)) != SASL_OK) + return ret; +#endif /* _SUN_SDK_ && USE_UEF */ + + /* reauth cache */ + reauth_cache = utils->malloc(sizeof(reauth_cache_t)); + if (reauth_cache == NULL) + return SASL_NOMEM; + memset(reauth_cache, 0, sizeof(reauth_cache_t)); + reauth_cache->i_am = CLIENT; + + /* mutex */ + reauth_cache->mutex = utils->mutex_alloc(); + if (!reauth_cache->mutex) + return SASL_FAIL; + + /* entries */ + reauth_cache->size = 10; + reauth_cache->e = utils->malloc(reauth_cache->size * + sizeof(reauth_entry_t)); + if (reauth_cache->e == NULL) + return SASL_NOMEM; + memset(reauth_cache->e, 0, reauth_cache->size * sizeof(reauth_entry_t)); + + digestmd5_client_plugins[0].glob_context = reauth_cache; +#ifdef _SUN_SDK_ +#ifdef USE_UEF_CLIENT + digestmd5_client_plugins[0].max_ssf = uef_max_ssf; +#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. + */ + REG_PLUG("DIGEST-MD5", digestmd5_client_plugins); +#endif /* _INTEGRATED_SOLARIS_ */ + /* CRYPT DELETE END */ + /* EXPORT DELETE END */ + + *out_version = SASL_CLIENT_PLUG_VERSION; + *pluglist = digestmd5_client_plugins; + *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 +getSlotID(const sasl_utils_t *utils, CK_MECHANISM_TYPE mech_type, + CK_SLOT_ID *slot_id) +{ + CK_RV rv; + CK_ULONG ulSlotCount; + CK_ULONG ulMechTypeCount; + CK_SLOT_ID *pSlotList = NULL; + CK_SLOT_ID slotID; + CK_MECHANISM_TYPE_PTR pMechTypeList = NULL; + int i, m; + + rv = C_GetSlotList(CK_FALSE, NULL_PTR, &ulSlotCount); + if (rv != CKR_OK || ulSlotCount == 0) { +#ifdef DEBUG + utils->log(utils->conn, SASL_LOG_DEBUG, + "C_GetSlotList: 0x%.8X count:%d\n", rv, ulSlotCount); +#endif + return SASL_FAIL; + } + + pSlotList = utils->calloc(sizeof (CK_SLOT_ID), ulSlotCount); + if (pSlotList == NULL) + return SASL_NOMEM; + + rv = C_GetSlotList(CK_FALSE, pSlotList, &ulSlotCount); + if (rv != CKR_OK) { +#ifdef DEBUG + utils->log(utils->conn, SASL_LOG_DEBUG, + "C_GetSlotList: 0x%.8X count:%d\n", rv, ulSlotCount); +#endif + return SASL_FAIL; + } + + for (i = 0; i < ulSlotCount; i++) { + slotID = pSlotList[i]; + rv = C_GetMechanismList(slotID, NULL_PTR, &ulMechTypeCount); + if (rv != CKR_OK) { +#ifdef DEBUG + utils->log(utils->conn, SASL_LOG_DEBUG, + "C_GetMechanismList returned 0x%.8X count:%d\n", rv, + ulMechTypeCount); +#endif + utils->free(pSlotList); + return SASL_FAIL; + } + pMechTypeList = + utils->calloc(sizeof (CK_MECHANISM_TYPE), ulMechTypeCount); + if (pMechTypeList == NULL_PTR) { + utils->free(pSlotList); + return SASL_NOMEM; + } + rv = C_GetMechanismList(slotID, pMechTypeList, &ulMechTypeCount); + if (rv != CKR_OK) { +#ifdef DEBUG + utils->log(utils->conn, SASL_LOG_DEBUG, + "C_GetMechanismList returned 0x%.8X count:%d\n", rv, + ulMechTypeCount); +#endif + utils->free(pMechTypeList); + utils->free(pSlotList); + return SASL_FAIL; + } + + for (m = 0; m < ulMechTypeCount; m++) { + if (pMechTypeList[m] == mech_type) + break; + } + utils->free(pMechTypeList); + pMechTypeList = NULL; + if (m < ulMechTypeCount) + break; + } + utils->free(pSlotList); + if (i < ulSlotCount) { + *slot_id = slotID; + return SASL_OK; + } + return SASL_FAIL; +} + +static int +uef_init(const sasl_utils_t *utils) +{ + int got_rc4; + int got_des; + int got_3des; + int next_c; + CK_RV rv; + + if (got_uef_slot) + return (SASL_OK); + + if (LOCK_MUTEX(&uef_init_mutex) < 0) + return (SASL_FAIL); + + rv = C_Initialize(NULL_PTR); + if (rv != CKR_OK && rv != CKR_CRYPTOKI_ALREADY_INITIALIZED) { +#ifdef DEBUG + utils->log(utils->conn, SASL_LOG_DEBUG, + "C_Initialize returned 0x%.8X\n", rv); +#endif + return SASL_FAIL; + } + + got_rc4 = getSlotID(utils, CKM_RC4, &rc4_slot_id) == SASL_OK; + if (!got_rc4) + utils->log(utils->conn, SASL_LOG_WARN, "Could not get rc4"); + + got_des = getSlotID(utils, CKM_DES_CBC, &des_slot_id) == SASL_OK; + if (!got_des) + utils->log(utils->conn, SASL_LOG_WARN, "Could not get des"); + + got_3des = getSlotID(utils, CKM_DES3_CBC, &des3_slot_id) == SASL_OK; + if (!got_3des) + utils->log(utils->conn, SASL_LOG_WARN, "Could not get 3des"); + + uef_max_ssf = got_rc4 ? 128 : got_3des ? 112 : got_des ? 55 : 0; + + /* adjust the available ciphers */ + next_c = (got_rc4) ? 3 : 0; + + if (got_des) { + uef_ciphers[next_c].name = uef_ciphers[DES_CIPHER_INDEX].name; + uef_ciphers[next_c].ssf = uef_ciphers[DES_CIPHER_INDEX].ssf; + uef_ciphers[next_c].n = uef_ciphers[DES_CIPHER_INDEX].n; + uef_ciphers[next_c].flag = uef_ciphers[DES_CIPHER_INDEX].flag; + uef_ciphers[next_c].cipher_enc = + uef_ciphers[DES_CIPHER_INDEX].cipher_enc; + uef_ciphers[next_c].cipher_dec = + uef_ciphers[DES_CIPHER_INDEX].cipher_dec; + uef_ciphers[next_c].cipher_init = + uef_ciphers[DES_CIPHER_INDEX].cipher_init; + next_c++; + } + + if (got_3des) { + uef_ciphers[next_c].name = uef_ciphers[DES3_CIPHER_INDEX].name; + uef_ciphers[next_c].ssf = uef_ciphers[DES3_CIPHER_INDEX].ssf; + uef_ciphers[next_c].n = uef_ciphers[DES3_CIPHER_INDEX].n; + uef_ciphers[next_c].flag = uef_ciphers[DES3_CIPHER_INDEX].flag; + uef_ciphers[next_c].cipher_enc = + uef_ciphers[DES3_CIPHER_INDEX].cipher_enc; + uef_ciphers[next_c].cipher_dec = + uef_ciphers[DES3_CIPHER_INDEX].cipher_dec; + uef_ciphers[next_c].cipher_init = + uef_ciphers[DES3_CIPHER_INDEX].cipher_init; + next_c++; + } + uef_ciphers[next_c].name = NULL; + + got_uef_slot = TRUE; + UNLOCK_MUTEX(&uef_init_mutex); + + return (SASL_OK); +} +#endif /* USE_UEF */ +#endif /* _SUN_SDK_ */ diff --git a/usr/src/lib/sasl_plugins/digestmd5/digestmd5_init.c b/usr/src/lib/sasl_plugins/digestmd5/digestmd5_init.c new file mode 100644 index 0000000000..ebb834a84a --- /dev/null +++ b/usr/src/lib/sasl_plugins/digestmd5/digestmd5_init.c @@ -0,0 +1,44 @@ +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <config.h> + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#ifndef macintosh +#include <sys/stat.h> +#endif +#include <fcntl.h> +#include <assert.h> + +#include <sasl.h> +#include <saslplug.h> +#include <saslutil.h> + +#include "plugin_common.h" + +#ifdef macintosh +#include <sasl_digestmd5_plugin_decl.h> +#endif + +#ifdef WIN32 +BOOL APIENTRY DllMain( HANDLE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved + ) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} +#endif + +SASL_CLIENT_PLUG_INIT( digestmd5 ) +SASL_SERVER_PLUG_INIT( digestmd5 ) + diff --git a/usr/src/lib/sasl_plugins/digestmd5/i386/Makefile b/usr/src/lib/sasl_plugins/digestmd5/i386/Makefile new file mode 100644 index 0000000000..c9036b2b2f --- /dev/null +++ b/usr/src/lib/sasl_plugins/digestmd5/i386/Makefile @@ -0,0 +1,11 @@ +# +# Copyright 2003 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +# lib/libsasl/plugins/digestmd5/i386/Makefile + +include ../Makefile.com + +install: all $(ROOTLIBS) diff --git a/usr/src/lib/sasl_plugins/digestmd5/sparc/Makefile b/usr/src/lib/sasl_plugins/digestmd5/sparc/Makefile new file mode 100644 index 0000000000..27e10f0022 --- /dev/null +++ b/usr/src/lib/sasl_plugins/digestmd5/sparc/Makefile @@ -0,0 +1,11 @@ +# +# Copyright 2003 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +# lib/libsasl/plugins/digest/sparc/Makefile + +include ../Makefile.com + +install: all $(ROOTLIBS) diff --git a/usr/src/lib/sasl_plugins/digestmd5/sparcv9/Makefile b/usr/src/lib/sasl_plugins/digestmd5/sparcv9/Makefile new file mode 100644 index 0000000000..7aa0a295fa --- /dev/null +++ b/usr/src/lib/sasl_plugins/digestmd5/sparcv9/Makefile @@ -0,0 +1,12 @@ +# +# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +# lib/libsasl/plugins/digest/sparcv9/Makefile + +include ../Makefile.com +include $(SRC)/lib/Makefile.lib.64 + +install: all $(ROOTLIBS64) diff --git a/usr/src/lib/sasl_plugins/digestmd5/spec/Makefile b/usr/src/lib/sasl_plugins/digestmd5/spec/Makefile new file mode 100644 index 0000000000..78e5a082a2 --- /dev/null +++ b/usr/src/lib/sasl_plugins/digestmd5/spec/Makefile @@ -0,0 +1,7 @@ +# +# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" + +include $(SRC)/lib/Makefile.spec.arch diff --git a/usr/src/lib/sasl_plugins/digestmd5/spec/Makefile.targ b/usr/src/lib/sasl_plugins/digestmd5/spec/Makefile.targ new file mode 100644 index 0000000000..bd86aec418 --- /dev/null +++ b/usr/src/lib/sasl_plugins/digestmd5/spec/Makefile.targ @@ -0,0 +1,10 @@ +# +# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" + +LIBRARY = digestmd5.a +VERS = .1 + +OBJECTS = digestmd5.o diff --git a/usr/src/lib/sasl_plugins/digestmd5/spec/amd64/Makefile b/usr/src/lib/sasl_plugins/digestmd5/spec/amd64/Makefile new file mode 100644 index 0000000000..07e64367c4 --- /dev/null +++ b/usr/src/lib/sasl_plugins/digestmd5/spec/amd64/Makefile @@ -0,0 +1,14 @@ +# +# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +.KEEP_STATE: + +include ../Makefile.targ + +include $(SRC)/lib/Makefile.lib +include $(SRC)/lib/Makefile.lib.64 +include $(SRC)/lib/Makefile.spec diff --git a/usr/src/lib/sasl_plugins/digestmd5/spec/digestmd5.spec b/usr/src/lib/sasl_plugins/digestmd5/spec/digestmd5.spec new file mode 100644 index 0000000000..aaa78d87df --- /dev/null +++ b/usr/src/lib/sasl_plugins/digestmd5/spec/digestmd5.spec @@ -0,0 +1,28 @@ +# +# Copyright 2003 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +# lib/libsasl/plugin/digestmd5/spec/digestmd5.spec +# + +function sasl_client_plug_init +include <sasl/saslplug.h> +declaration int sasl_client_plug_init(const sasl_utils_t *utils, \ + int maxversion, int *out_version, \ + sasl_client_plug_t **pluglist, \ + int *plugcount) +exception $return == SASL_FAIL +version SUNWprivate_1.1 +end + +function sasl_server_plug_init +include <sasl/saslplug.h> +declaration int sasl_server_plug_init(const sasl_utils_t *utils, \ + int maxversion, int *out_version, \ + sasl_server_plug_t **pluglist, \ + int *plugcount) +exception $return == SASL_FAIL +version SUNWprivate_1.1 +end diff --git a/usr/src/lib/sasl_plugins/digestmd5/spec/i386/Makefile b/usr/src/lib/sasl_plugins/digestmd5/spec/i386/Makefile new file mode 100644 index 0000000000..898bd186dc --- /dev/null +++ b/usr/src/lib/sasl_plugins/digestmd5/spec/i386/Makefile @@ -0,0 +1,15 @@ +# +# Copyright 2003 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# lib/libsasl/plugins/digestmd5/spec/i386/Makefile +# + +.KEEP_STATE: + +include ../Makefile.targ + +include $(SRC)/lib/Makefile.lib +include $(SRC)/lib/Makefile.spec diff --git a/usr/src/lib/sasl_plugins/digestmd5/spec/sparc/Makefile b/usr/src/lib/sasl_plugins/digestmd5/spec/sparc/Makefile new file mode 100644 index 0000000000..d98d83e258 --- /dev/null +++ b/usr/src/lib/sasl_plugins/digestmd5/spec/sparc/Makefile @@ -0,0 +1,15 @@ +# +# Copyright 2003 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# lib/libsasl/plugins/digestmd5/spec/sparc/Makefile +# + +.KEEP_STATE: + +include ../Makefile.targ + +include $(SRC)/lib/Makefile.lib +include $(SRC)/lib/Makefile.spec diff --git a/usr/src/lib/sasl_plugins/digestmd5/spec/sparcv9/Makefile b/usr/src/lib/sasl_plugins/digestmd5/spec/sparcv9/Makefile new file mode 100644 index 0000000000..63a659bc17 --- /dev/null +++ b/usr/src/lib/sasl_plugins/digestmd5/spec/sparcv9/Makefile @@ -0,0 +1,16 @@ +# +# Copyright 2003 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# lib/libsasl/plugins/digestmd5/spec/sparcv9/Makefile +# + +.KEEP_STATE: + +include ../Makefile.targ + +include $(SRC)/lib/Makefile.lib +include $(SRC)/lib/Makefile.lib.64 +include $(SRC)/lib/Makefile.spec diff --git a/usr/src/lib/sasl_plugins/digestmd5/spec/versions b/usr/src/lib/sasl_plugins/digestmd5/spec/versions new file mode 100644 index 0000000000..6a318d48a3 --- /dev/null +++ b/usr/src/lib/sasl_plugins/digestmd5/spec/versions @@ -0,0 +1,19 @@ +# +# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +sparc { + SUNWprivate_1.1; +} +sparcv9 { + SUNWprivate_1.1; +} +i386 { + SUNWprivate_1.1; +} +amd64 { + SUNWprivate_1.1; +} diff --git a/usr/src/lib/sasl_plugins/gssapi/Makefile b/usr/src/lib/sasl_plugins/gssapi/Makefile new file mode 100644 index 0000000000..4897cf88f3 --- /dev/null +++ b/usr/src/lib/sasl_plugins/gssapi/Makefile @@ -0,0 +1,7 @@ +# +# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" + +include ../Makefile.subdirs diff --git a/usr/src/lib/sasl_plugins/gssapi/Makefile.com b/usr/src/lib/sasl_plugins/gssapi/Makefile.com new file mode 100644 index 0000000000..413208d5ed --- /dev/null +++ b/usr/src/lib/sasl_plugins/gssapi/Makefile.com @@ -0,0 +1,15 @@ +# +# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" + +LIBRARY= gssapi.a +VERS= .1 + +PLUG_OBJS= gssapi.o gssapiv2_init.o + +PLUG_LIBS = -lgss + +# include common definitions +include ../../Makefile.com diff --git a/usr/src/lib/sasl_plugins/gssapi/amd64/Makefile b/usr/src/lib/sasl_plugins/gssapi/amd64/Makefile new file mode 100644 index 0000000000..13ae0db12d --- /dev/null +++ b/usr/src/lib/sasl_plugins/gssapi/amd64/Makefile @@ -0,0 +1,11 @@ +# +# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +include ../Makefile.com +include ../../../Makefile.lib.64 + +install: all $(ROOTLIBS64) diff --git a/usr/src/lib/sasl_plugins/gssapi/gssapi.c b/usr/src/lib/sasl_plugins/gssapi/gssapi.c new file mode 100644 index 0000000000..302d029380 --- /dev/null +++ b/usr/src/lib/sasl_plugins/gssapi/gssapi.c @@ -0,0 +1,2206 @@ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* GSSAPI SASL plugin + * Leif Johansson + * Rob Siemborski (SASL v2 Conversion) + * $Id: gssapi.c,v 1.75 2003/07/02 13:13:42 rjs3 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 + * the documentation and/or other materials provided with the + * 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> + +#ifdef HAVE_GSSAPI_H +#include <gssapi.h> +#else +#include <gssapi/gssapi.h> +#endif + +#ifdef WIN32 +# include <winsock.h> + +# ifndef R_OK +# define R_OK 04 +# endif +/* we also need io.h for access() prototype */ +# include <io.h> +#else +# include <sys/param.h> +# include <sys/socket.h> +# include <netinet/in.h> +# include <arpa/inet.h> +# include <netdb.h> +#endif /* WIN32 */ +#include <fcntl.h> +#include <stdio.h> +#include <sasl.h> +#include <saslutil.h> +#include <saslplug.h> + +#include "plugin_common.h" + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#include <errno.h> + +#ifdef WIN32 +/* This must be after sasl.h */ +# include "saslgssapi.h" +#endif /* WIN32 */ + +/***************************** Common Section *****************************/ + +#ifndef _SUN_SDK_ +static const char plugin_id[] = "$Id: gssapi.c,v 1.75 2003/07/02 13:13:42 rjs3 Exp $"; +#endif /* !_SUN_SDK_ */ + +static const char * GSSAPI_BLANK_STRING = ""; + +#ifndef HAVE_GSS_C_NT_HOSTBASED_SERVICE +extern gss_OID gss_nt_service_name; +#define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name +#endif + +#ifdef _SUN_SDK_ +static int +get_oid(const sasl_utils_t *utils, gss_OID *oid); +#ifdef GSSAPI_PROTECT +DEFINE_STATIC_MUTEX(global_mutex); +#endif /* GSSAPI_PROTECT */ +#endif /* _SUN_SDK_ */ + +/* GSSAPI SASL Mechanism by Leif Johansson <leifj@matematik.su.se> + * inspired by the kerberos mechanism and the gssapi_server and + * gssapi_client from the heimdal distribution by Assar Westerlund + * <assar@sics.se> and Johan Danielsson <joda@pdc.kth.se>. + * See the configure.in file for details on dependencies. + * Heimdal can be obtained from http://www.pdc.kth.se/heimdal + * + * Important contributions from Sam Hartman <hartmans@fundsxpress.com>. + */ + +typedef struct context { + int state; + + gss_ctx_id_t gss_ctx; + gss_name_t client_name; + gss_name_t server_name; + gss_cred_id_t server_creds; + sasl_ssf_t limitssf, requiressf; /* application defined bounds, for the + server */ +#ifdef _SUN_SDK_ + gss_cred_id_t client_creds; + gss_OID mech_oid; + int use_authid; +#endif /* _SUN_SDK_ */ + const sasl_utils_t *utils; + + /* layers buffering */ + char *buffer; +#ifdef _SUN_SDK_ + unsigned bufsize; +#else + int bufsize; +#endif /* _SUN_SDK_ */ + char sizebuf[4]; +#ifdef _SUN_SDK_ + unsigned cursize; + unsigned size; +#else + int cursize; + int size; +#endif /* _SUN_SDK_ */ + unsigned needsize; + + char *encode_buf; /* For encoding/decoding mem management */ + char *decode_buf; + char *decode_once_buf; + unsigned encode_buf_len; + unsigned decode_buf_len; + unsigned decode_once_buf_len; + buffer_info_t *enc_in_buf; + + char *out_buf; /* per-step mem management */ + unsigned out_buf_len; + + char *authid; /* hold the authid between steps - server */ + const char *user; /* hold the userid between steps - client */ +#ifdef _SUN_SDK_ + const char *client_authid; +#endif /* _SUN_SDK_ */ +#ifdef _INTEGRATED_SOLARIS_ + void *h; +#endif /* _INTEGRATED_SOLARIS_ */ +} context_t; + +enum { + SASL_GSSAPI_STATE_AUTHNEG = 1, + SASL_GSSAPI_STATE_SSFCAP = 2, + SASL_GSSAPI_STATE_SSFREQ = 3, + SASL_GSSAPI_STATE_AUTHENTICATED = 4 +}; + +#ifdef _SUN_SDK_ +/* sasl_gss_log only logs gss_display_status() error string */ +#define sasl_gss_log(x,y,z) sasl_gss_seterror_(text,y,z,1) +#define sasl_gss_seterror(x,y,z) sasl_gss_seterror_(text,y,z,0) +static void +sasl_gss_seterror_(const context_t *text, OM_uint32 maj, OM_uint32 min, + int logonly) +#else +static void +sasl_gss_seterror(const sasl_utils_t *utils, OM_uint32 maj, OM_uint32 min) +#endif /* _SUN_SDK_ */ +{ + OM_uint32 maj_stat, min_stat; + gss_buffer_desc msg; + OM_uint32 msg_ctx; + int ret; + char *out = NULL; +#ifdef _SUN_SDK_ + unsigned len, curlen = 0; + const sasl_utils_t *utils = text->utils; + char *prefix = dgettext(TEXT_DOMAIN, "GSSAPI Error: "); +#else + size_t len, curlen = 0; + const char prefix[] = "GSSAPI Error: "; +#endif /* _SUN_SDK_ */ + + if(!utils) return; + + len = sizeof(prefix); + ret = _plug_buf_alloc(utils, &out, &curlen, 256); + if(ret != SASL_OK) return; + + strcpy(out, prefix); + + msg_ctx = 0; + while (1) { + maj_stat = gss_display_status(&min_stat, maj, +#ifdef _SUN_SDK_ + GSS_C_GSS_CODE, text->mech_oid, +#else + GSS_C_GSS_CODE, GSS_C_NULL_OID, +#endif /* _SUN_SDK_ */ + &msg_ctx, &msg); + if(GSS_ERROR(maj_stat)) { +#ifdef _SUN_SDK_ + if (logonly) { + utils->log(text->utils->conn, SASL_LOG_FAIL, + "GSSAPI Failure: (could not get major error message)"); + } else { +#endif /* _SUN_SDK_ */ +#ifdef _INTEGRATED_SOLARIS_ + utils->seterror(utils->conn, 0, + gettext("GSSAPI Failure " + "(could not get major error message)")); +#ifdef _SUN_SDK_ + } +#endif /* _SUN_SDK_ */ +#else + utils->seterror(utils->conn, 0, + "GSSAPI Failure " + "(could not get major error message)"); +#ifdef _SUN_SDK_ + } +#endif /* _SUN_SDK_ */ +#endif /* _INTEGRATED_SOLARIS_ */ + utils->free(out); + return; + } + + len += len + msg.length; + ret = _plug_buf_alloc(utils, &out, &curlen, len); + + if(ret != SASL_OK) { + utils->free(out); + return; + } + + strcat(out, msg.value); + + gss_release_buffer(&min_stat, &msg); + + if (!msg_ctx) + break; + } + + /* Now get the minor status */ + + len += 2; + ret = _plug_buf_alloc(utils, &out, &curlen, len); + if(ret != SASL_OK) { + utils->free(out); + return; + } + + strcat(out, " ("); + + msg_ctx = 0; + while (1) { + maj_stat = gss_display_status(&min_stat, min, +#ifdef _SUN_SDK_ + GSS_C_MECH_CODE, text->mech_oid, +#else + GSS_C_MECH_CODE, GSS_C_NULL_OID, +#endif /* _SUN_SDK_ */ + &msg_ctx, &msg); + if(GSS_ERROR(maj_stat)) { +#ifdef _SUN_SDK_ + if (logonly) { + utils->log(text->utils->conn, SASL_LOG_FAIL, + "GSSAPI Failure: (could not get minor error message)"); + } else { +#endif /* _SUN_SDK_ */ +#ifdef _INTEGRATED_SOLARIS_ + utils->seterror(utils->conn, 0, + gettext("GSSAPI Failure " + "(could not get minor error message)")); +#ifdef _SUN_SDK_ + } +#endif /* _SUN_SDK_ */ +#else + utils->seterror(utils->conn, 0, + "GSSAPI Failure " + "(could not get minor error message)"); +#ifdef _SUN_SDK_ + } +#endif /* _SUN_SDK_ */ +#endif /* _INTEGRATED_SOLARIS_ */ + utils->free(out); + return; + } + + len += len + msg.length; + ret = _plug_buf_alloc(utils, &out, &curlen, len); + + if(ret != SASL_OK) { + utils->free(out); + return; + } + + strcat(out, msg.value); + + gss_release_buffer(&min_stat, &msg); + + if (!msg_ctx) + break; + } + + len += 1; + ret = _plug_buf_alloc(utils, &out, &curlen, len); + if(ret != SASL_OK) { + utils->free(out); + return; + } + + strcat(out, ")"); + +#ifdef _SUN_SDK_ + if (logonly) { + utils->log(text->utils->conn, SASL_LOG_FAIL, out); + } else { + utils->seterror(utils->conn, 0, out); + } +#else + utils->seterror(utils->conn, 0, out); +#endif /* _SUN_SDK_ */ + utils->free(out); +} + +static int +sasl_gss_encode(void *context, const struct iovec *invec, unsigned numiov, + const char **output, unsigned *outputlen, int privacy) +{ + context_t *text = (context_t *)context; + OM_uint32 maj_stat, min_stat; + gss_buffer_t input_token, output_token; + gss_buffer_desc real_input_token, real_output_token; + int ret; + struct buffer_info *inblob, bufinfo; + + if(!output) return SASL_BADPARAM; + + if(numiov > 1) { + ret = _plug_iovec_to_buf(text->utils, invec, numiov, &text->enc_in_buf); + if(ret != SASL_OK) return ret; + inblob = text->enc_in_buf; + } else { + bufinfo.data = invec[0].iov_base; + bufinfo.curlen = invec[0].iov_len; + inblob = &bufinfo; + } + + if (text->state != SASL_GSSAPI_STATE_AUTHENTICATED) return SASL_NOTDONE; + + input_token = &real_input_token; + + real_input_token.value = inblob->data; + real_input_token.length = inblob->curlen; + + output_token = &real_output_token; + output_token->value = NULL; + output_token->length = 0; + +#if defined _SUN_SDK_ && defined GSSAPI_PROTECT + if (LOCK_MUTEX(&global_mutex) < 0) + return (SASL_FAIL); +#endif /* _SUN_SDK_ && GSSAPI_PROTECT */ + maj_stat = gss_wrap (&min_stat, + text->gss_ctx, + privacy, + GSS_C_QOP_DEFAULT, + input_token, + NULL, + output_token); + + if (GSS_ERROR(maj_stat)) + { + sasl_gss_seterror(text->utils, maj_stat, min_stat); + if (output_token->value) + gss_release_buffer(&min_stat, output_token); +#if defined _SUN_SDK_ && defined GSSAPI_PROTECT + UNLOCK_MUTEX(&global_mutex); +#endif /* _SUN_SDK_ && GSSAPI_PROTECT */ + return SASL_FAIL; + } + + if (output_token->value && output) { + int len; + + ret = _plug_buf_alloc(text->utils, &(text->encode_buf), + &(text->encode_buf_len), output_token->length + 4); + + if (ret != SASL_OK) { + gss_release_buffer(&min_stat, output_token); +#if defined _SUN_SDK_ && defined GSSAPI_PROTECT + UNLOCK_MUTEX(&global_mutex); +#endif /* _SUN_SDK_ && GSSAPI_PROTECT */ + return ret; + } + + len = htonl(output_token->length); + memcpy(text->encode_buf, &len, 4); + memcpy(text->encode_buf + 4, output_token->value, output_token->length); + } + + if (outputlen) { + *outputlen = output_token->length + 4; + } + + *output = text->encode_buf; + + if (output_token->value) + gss_release_buffer(&min_stat, output_token); + +#if defined _SUN_SDK_ && defined GSSAPI_PROTECT + UNLOCK_MUTEX(&global_mutex); +#endif /* _SUN_SDK_ && GSSAPI_PROTECT */ + + return SASL_OK; +} + +static int gssapi_privacy_encode(void *context, const struct iovec *invec, + unsigned numiov, const char **output, + unsigned *outputlen) +{ + return sasl_gss_encode(context,invec,numiov,output,outputlen,1); +} + +static int gssapi_integrity_encode(void *context, const struct iovec *invec, + unsigned numiov, const char **output, + unsigned *outputlen) +{ + return sasl_gss_encode(context,invec,numiov,output,outputlen,0); +} + +#define myMIN(a,b) (((a) < (b)) ? (a) : (b)) + +static int gssapi_decode_once(void *context, + const char **input, unsigned *inputlen, + char **output, unsigned *outputlen) +{ + context_t *text = (context_t *) context; + OM_uint32 maj_stat, min_stat; + gss_buffer_t input_token, output_token; + gss_buffer_desc real_input_token, real_output_token; + int result; + unsigned diff; + + if (text->state != SASL_GSSAPI_STATE_AUTHENTICATED) { +#ifdef _INTEGRATED_SOLARIS_ + SETERROR(text->utils, gettext("GSSAPI Failure")); +#else + SETERROR(text->utils, "GSSAPI Failure"); +#endif /* _INTEGRATED_SOLARIS_ */ + return SASL_NOTDONE; + } + + /* first we need to extract a packet */ + if (text->needsize > 0) { + /* how long is it? */ + int tocopy = myMIN(text->needsize, *inputlen); + + memcpy(text->sizebuf + 4 - text->needsize, *input, tocopy); + text->needsize -= tocopy; + *input += tocopy; + *inputlen -= tocopy; + + if (text->needsize == 0) { + /* got the entire size */ + memcpy(&text->size, text->sizebuf, 4); + text->size = ntohl(text->size); + text->cursize = 0; + +#ifdef _SUN_SDK_ + if (text->size > 0xFFFFFF) { + text->utils->log(text->utils->conn, SASL_LOG_ERR, + "Illegal size in sasl_gss_decode_once"); +#else + if (text->size > 0xFFFFFF || text->size <= 0) { + SETERROR(text->utils, "Illegal size in sasl_gss_decode_once"); +#endif /* _SUN_SDK_ */ + return SASL_FAIL; + } + + if (text->bufsize < text->size + 5) { + result = _plug_buf_alloc(text->utils, &text->buffer, + &(text->bufsize), text->size+5); + if(result != SASL_OK) return result; + } + } + if (*inputlen == 0) { + /* need more data ! */ + *outputlen = 0; + *output = NULL; + + return SASL_OK; + } + } + + diff = text->size - text->cursize; + + if (*inputlen < diff) { + /* ok, let's queue it up; not enough data */ + memcpy(text->buffer + text->cursize, *input, *inputlen); + text->cursize += *inputlen; + *inputlen = 0; + *outputlen = 0; + *output = NULL; + return SASL_OK; + } else { + memcpy(text->buffer + text->cursize, *input, diff); + *input += diff; + *inputlen -= diff; + } + + input_token = &real_input_token; + real_input_token.value = text->buffer; + real_input_token.length = text->size; + + output_token = &real_output_token; + output_token->value = NULL; + output_token->length = 0; + +#if defined _SUN_SDK_ && defined GSSAPI_PROTECT + if (LOCK_MUTEX(&global_mutex) < 0) + return (SASL_FAIL); +#endif /* _SUN_SDK_ && GSSAPI_PROTECT */ + + maj_stat = gss_unwrap (&min_stat, + text->gss_ctx, + input_token, + output_token, + NULL, + NULL); + + if (GSS_ERROR(maj_stat)) + { + sasl_gss_seterror(text->utils, maj_stat, min_stat); + if (output_token->value) + gss_release_buffer(&min_stat, output_token); +#if defined _SUN_SDK_ && defined GSSAPI_PROTECT + UNLOCK_MUTEX(&global_mutex); +#endif /* _SUN_SDK_ && GSSAPI_PROTECT */ + return SASL_FAIL; + } + + if (outputlen) + *outputlen = output_token->length; + + if (output_token->value) { + if (output) { + result = _plug_buf_alloc(text->utils, &text->decode_once_buf, + &text->decode_once_buf_len, + *outputlen); + if(result != SASL_OK) { + gss_release_buffer(&min_stat, output_token); +#if defined _SUN_SDK_ && defined GSSAPI_PROTECT + UNLOCK_MUTEX(&global_mutex); +#endif /* _SUN_SDK_ && GSSAPI_PROTECT */ + return result; + } + *output = text->decode_once_buf; + memcpy(*output, output_token->value, *outputlen); + } + gss_release_buffer(&min_stat, output_token); + } +#if defined _SUN_SDK_ && defined GSSAPI_PROTECT + UNLOCK_MUTEX(&global_mutex); +#endif /* _SUN_SDK_ && GSSAPI_PROTECT */ + + /* reset for the next packet */ +#ifndef _SUN_SDK_ + text->size = -1; +#endif /* !_SUN_SDK_ */ + text->needsize = 4; + + return SASL_OK; +} + +static int gssapi_decode(void *context, + const char *input, unsigned inputlen, + const char **output, unsigned *outputlen) +{ + context_t *text = (context_t *) context; + int ret; + + ret = _plug_decode(text->utils, context, input, inputlen, + &text->decode_buf, &text->decode_buf_len, outputlen, + gssapi_decode_once); + + *output = text->decode_buf; + + return ret; +} + +static context_t *gss_new_context(const sasl_utils_t *utils) +{ + context_t *ret; + + ret = utils->malloc(sizeof(context_t)); + if(!ret) return NULL; + + memset(ret,0,sizeof(context_t)); + ret->utils = utils; +#ifdef _SUN_SDK_ + ret->gss_ctx = GSS_C_NO_CONTEXT; + ret->client_name = GSS_C_NO_NAME; + ret->server_name = GSS_C_NO_NAME; + ret->server_creds = GSS_C_NO_CREDENTIAL; + ret->client_creds = GSS_C_NO_CREDENTIAL; + if (get_oid(utils, &ret->mech_oid) != SASL_OK) { + utils->free(ret); + return (NULL); + } +#endif /* _SUN_SDK_ */ + + ret->needsize = 4; + + return ret; +} + +static void sasl_gss_free_context_contents(context_t *text) +{ + OM_uint32 maj_stat, min_stat; + + if (!text) return; + + if (text->gss_ctx != GSS_C_NO_CONTEXT) { + maj_stat = gss_delete_sec_context (&min_stat,&text->gss_ctx,GSS_C_NO_BUFFER); + text->gss_ctx = GSS_C_NO_CONTEXT; + } + + if (text->client_name != GSS_C_NO_NAME) { + maj_stat = gss_release_name(&min_stat,&text->client_name); + text->client_name = GSS_C_NO_NAME; + } + + if (text->server_name != GSS_C_NO_NAME) { + maj_stat = gss_release_name(&min_stat,&text->server_name); + text->server_name = GSS_C_NO_NAME; + } + + if ( text->server_creds != GSS_C_NO_CREDENTIAL) { + maj_stat = gss_release_cred(&min_stat, &text->server_creds); + text->server_creds = GSS_C_NO_CREDENTIAL; + } + +#ifdef _SUN_SDK_ + if ( text->client_creds != GSS_C_NO_CREDENTIAL) { + maj_stat = gss_release_cred(&min_stat, &text->client_creds); + text->client_creds = GSS_C_NO_CREDENTIAL; + } + + /* + * Note that the oid returned by rpc_gss_mech_to_oid should not + * be released + */ +#endif /* _SUN_SDK_ */ + + if (text->out_buf) { + text->utils->free(text->out_buf); + text->out_buf = NULL; + } + + if (text->encode_buf) { + text->utils->free(text->encode_buf); + text->encode_buf = NULL; + } + + if (text->decode_buf) { + text->utils->free(text->decode_buf); + text->decode_buf = NULL; + } + + if (text->decode_once_buf) { + text->utils->free(text->decode_once_buf); + text->decode_once_buf = NULL; + } + + if (text->enc_in_buf) { + if(text->enc_in_buf->data) text->utils->free(text->enc_in_buf->data); + text->utils->free(text->enc_in_buf); + text->enc_in_buf = NULL; + } + + if (text->buffer) { + text->utils->free(text->buffer); + text->buffer = NULL; + } + + if (text->authid) { /* works for both client and server */ + text->utils->free(text->authid); + text->authid = NULL; + } +} + +#ifdef _SUN_SDK_ + +#ifdef HAVE_RPC_GSS_MECH_TO_OID +#include <rpc/rpcsec_gss.h> +#endif /* HAVE_RPC_GSS_MECH_TO_OID */ + +static int +get_oid(const sasl_utils_t *utils, gss_OID *oid) +{ +#ifdef HAVE_RPC_GSS_MECH_TO_OID + static gss_OID_desc kerb_v5 = + {9, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02"}; + /* 1.2.840.113554.1.2.2 */ + *oid = &kerb_v5; +#endif /* HAVE_RPC_GSS_MECH_TO_OID */ + return (SASL_OK); +} + +static int +add_mech_to_set(context_t *text, gss_OID_set *desired_mechs) +{ + OM_uint32 maj_stat, min_stat; + + maj_stat = gss_create_empty_oid_set(&min_stat, desired_mechs); + + if (GSS_ERROR(maj_stat)) { + sasl_gss_seterror(text->utils, maj_stat, min_stat); + sasl_gss_free_context_contents(text); + return SASL_FAIL; + } + + maj_stat = gss_add_oid_set_member(&min_stat, text->mech_oid, desired_mechs); + if (GSS_ERROR(maj_stat)) { + sasl_gss_seterror(text->utils, maj_stat, min_stat); + sasl_gss_free_context_contents(text); + (void) gss_release_oid_set(&min_stat, desired_mechs); + return SASL_FAIL; + } + return SASL_OK; +} +#endif /* _SUN_SDK_ */ + +static void gssapi_common_mech_dispose(void *conn_context, + const sasl_utils_t *utils) +{ +#ifdef _SUN_SDK_ + if (conn_context == NULL) + return; +#ifdef _INTEGRATED_SOLARIS_ + convert_prompt(utils, &((context_t *)conn_context)->h, NULL); +#endif /* _INTEGRATED_SOLARIS_ */ +#endif /* _SUN_SDK_ */ +#if defined _SUN_SDK_ && defined GSSAPI_PROTECT + (void) LOCK_MUTEX(&global_mutex); +#endif /* _SUN_SDK_ && GSSAPI_PROTECT */ + sasl_gss_free_context_contents((context_t *)(conn_context)); +#if defined _SUN_SDK_ && defined GSSAPI_PROTECT + UNLOCK_MUTEX(&global_mutex); +#endif /* _SUN_SDK_ && GSSAPI_PROTECT */ + utils->free(conn_context); +} + +/***************************** Server Section *****************************/ + +static int +gssapi_server_mech_new(void *glob_context __attribute__((unused)), + sasl_server_params_t *params, + const char *challenge __attribute__((unused)), + unsigned challen __attribute__((unused)), + void **conn_context) +{ + context_t *text; + +#if defined _SUN_SDK_ && defined GSSAPI_PROTECT + if (LOCK_MUTEX(&global_mutex) < 0) + return (SASL_FAIL); +#endif /* _SUN_SDK_ && GSSAPI_PROTECT */ + text = gss_new_context(params->utils); +#if defined _SUN_SDK_ && defined GSSAPI_PROTECT + UNLOCK_MUTEX(&global_mutex); +#endif /* _SUN_SDK_ && GSSAPI_PROTECT */ + if (text == NULL) { +#ifndef _SUN_SDK_ + MEMERROR(params->utils); +#endif /* !_SUN_SDK_ */ + return SASL_NOMEM; + } + + text->gss_ctx = GSS_C_NO_CONTEXT; + text->client_name = GSS_C_NO_NAME; + text->server_name = GSS_C_NO_NAME; + text->server_creds = GSS_C_NO_CREDENTIAL; + text->state = SASL_GSSAPI_STATE_AUTHNEG; + + *conn_context = text; + + return SASL_OK; +} + +static int +gssapi_server_mech_step(void *conn_context, + sasl_server_params_t *params, + const char *clientin, + unsigned clientinlen, + const char **serverout, + unsigned *serveroutlen, + sasl_out_params_t *oparams) +{ + context_t *text = (context_t *)conn_context; + gss_buffer_t input_token, output_token; + gss_buffer_desc real_input_token, real_output_token; + OM_uint32 maj_stat, min_stat; +#ifdef _SUN_SDK_ + OM_uint32 max_input_size; + gss_OID_set desired_mechs = GSS_C_NULL_OID_SET; +#endif /* _SUN_SDK_ */ + gss_buffer_desc name_token; + int ret; + + input_token = &real_input_token; + output_token = &real_output_token; + output_token->value = NULL; output_token->length = 0; + input_token->value = NULL; input_token->length = 0; + + if(!serverout) { + PARAMERROR(text->utils); + return SASL_BADPARAM; + } + + *serverout = NULL; + *serveroutlen = 0; + + switch (text->state) { + + case SASL_GSSAPI_STATE_AUTHNEG: + if (text->server_name == GSS_C_NO_NAME) { /* only once */ + name_token.length = strlen(params->service) + 1 + strlen(params->serverFQDN); + name_token.value = (char *)params->utils->malloc((name_token.length + 1) * sizeof(char)); + if (name_token.value == NULL) { + MEMERROR(text->utils); + sasl_gss_free_context_contents(text); + return SASL_NOMEM; + } +#ifdef _SUN_SDK_ + snprintf(name_token.value, name_token.length + 1, + "%s@%s", params->service, params->serverFQDN); +#else + sprintf(name_token.value,"%s@%s", params->service, params->serverFQDN); +#endif /* _SUN_SDK_ */ + + maj_stat = gss_import_name (&min_stat, + &name_token, + GSS_C_NT_HOSTBASED_SERVICE, + &text->server_name); + + params->utils->free(name_token.value); + name_token.value = NULL; + + if (GSS_ERROR(maj_stat)) { + sasl_gss_seterror(text->utils, maj_stat, min_stat); + sasl_gss_free_context_contents(text); + return SASL_FAIL; + } + + if ( text->server_creds != GSS_C_NO_CREDENTIAL) { + maj_stat = gss_release_cred(&min_stat, &text->server_creds); + text->server_creds = GSS_C_NO_CREDENTIAL; + } + +#ifdef _SUN_SDK_ + if (text->mech_oid != GSS_C_NULL_OID) { + ret = add_mech_to_set(text, &desired_mechs); + if (ret != SASL_OK) + return (ret); + } +#endif /* _SUN_SDK_ */ + + maj_stat = gss_acquire_cred(&min_stat, + text->server_name, + GSS_C_INDEFINITE, +#ifdef _SUN_SDK_ + desired_mechs, +#else + GSS_C_NO_OID_SET, +#endif /* _SUN_SDK_ */ + GSS_C_ACCEPT, + &text->server_creds, + NULL, + NULL); + +#ifdef _SUN_SDK_ + if (desired_mechs != GSS_C_NULL_OID_SET) { + OM_uint32 min_stat2; + (void) gss_release_oid_set(&min_stat2, &desired_mechs); + } +#endif /* _SUN_SDK_ */ + + if (GSS_ERROR(maj_stat)) { + sasl_gss_seterror(text->utils, maj_stat, min_stat); + sasl_gss_free_context_contents(text); + return SASL_FAIL; + } + } + + if (clientinlen) { + real_input_token.value = (void *)clientin; + real_input_token.length = clientinlen; + } + + maj_stat = + gss_accept_sec_context(&min_stat, + &(text->gss_ctx), + text->server_creds, + input_token, + GSS_C_NO_CHANNEL_BINDINGS, + &text->client_name, + NULL, + output_token, + NULL, + NULL, + NULL); + + if (GSS_ERROR(maj_stat)) { +#ifdef _SUN_SDK_ + /* log the local error info, set a more generic error */ + sasl_gss_log(text->utils, maj_stat, min_stat); + text->utils->seterror(text->utils->conn, SASL_NOLOG, + gettext("GSSAPI Failure: accept security context error")); + if (output_token->value) { + gss_release_buffer(&min_stat, output_token); + } +#else + if (output_token->value) { + gss_release_buffer(&min_stat, output_token); + } + text->utils->seterror(text->utils->conn, SASL_NOLOG, "GSSAPI Failure: gss_accept_sec_context"); + text->utils->log(NULL, SASL_LOG_DEBUG, "GSSAPI Failure: gss_accept_sec_context"); +#endif /* _SUN_SDK_ */ + sasl_gss_free_context_contents(text); + return SASL_BADAUTH; + } + + if (serveroutlen) + *serveroutlen = output_token->length; + if (output_token->value) { + if (serverout) { + ret = _plug_buf_alloc(text->utils, &(text->out_buf), + &(text->out_buf_len), *serveroutlen); + if(ret != SASL_OK) { + gss_release_buffer(&min_stat, output_token); + return ret; + } + memcpy(text->out_buf, output_token->value, *serveroutlen); + *serverout = text->out_buf; + } + + gss_release_buffer(&min_stat, output_token); + } else { + /* No output token, send an empty string */ + *serverout = GSSAPI_BLANK_STRING; +#ifndef _SUN_SDK_ + serveroutlen = 0; +#endif /* !_SUN_SDK_ */ + } + + + if (maj_stat == GSS_S_COMPLETE) { + /* Switch to ssf negotiation */ + text->state = SASL_GSSAPI_STATE_SSFCAP; + } + + return SASL_CONTINUE; + + case SASL_GSSAPI_STATE_SSFCAP: { + unsigned char sasldata[4]; + gss_buffer_desc name_token; +#ifndef _SUN_SDK_ + gss_buffer_desc name_without_realm; + gss_name_t without = NULL; + int equal; +#endif /* !_SUN_SDK_ */ + + name_token.value = NULL; +#ifndef _SUN_SDK_ + name_without_realm.value = NULL; +#endif /* !_SUN_SDK_ */ + + /* We ignore whatever the client sent us at this stage */ + + maj_stat = gss_display_name (&min_stat, + text->client_name, + &name_token, + NULL); + + if (GSS_ERROR(maj_stat)) { +#ifndef _SUN_SDK_ + if (name_without_realm.value) + params->utils->free(name_without_realm.value); +#endif /* !_SUN_SDK_ */ + + if (name_token.value) + gss_release_buffer(&min_stat, &name_token); +#ifndef _SUN_SDK_ + if (without) + gss_release_name(&min_stat, &without); +#endif /* !_SUN_SDK_ */ +#ifdef _INTEGRATED_SOLARIS_ + SETERROR(text->utils, gettext("GSSAPI Failure")); +#else + SETERROR(text->utils, "GSSAPI Failure"); +#endif /* _INTEGRATED_SOLARIS_ */ + sasl_gss_free_context_contents(text); + return SASL_BADAUTH; + } + +#ifndef _SUN_SDK_ + /* If the id contains a realm get the identifier for the user + without the realm and see if it's the same id (i.e. + tmartin == tmartin@ANDREW.CMU.EDU. If this is the case we just want + to return the id (i.e. just "tmartin" */ + if (strchr((char *) name_token.value, (int) '@') != NULL) { + /* NOTE: libc malloc, as it is freed below by a gssapi internal + * function! */ + name_without_realm.value = malloc(strlen(name_token.value)+1); + if (name_without_realm.value == NULL) { + MEMERROR(text->utils); + return SASL_NOMEM; + } + + strcpy(name_without_realm.value, name_token.value); + + /* cut off string at '@' */ + (strchr(name_without_realm.value,'@'))[0] = '\0'; + + name_without_realm.length = strlen( (char *) name_without_realm.value ); + + maj_stat = gss_import_name (&min_stat, + &name_without_realm, + /* Solaris 8/9 gss_import_name doesn't accept GSS_C_NULL_OID here, + so use GSS_C_NT_USER_NAME instead if available. */ +#ifdef HAVE_GSS_C_NT_USER_NAME + GSS_C_NT_USER_NAME, +#else + GSS_C_NULL_OID, +#endif + &without); + + if (GSS_ERROR(maj_stat)) { + params->utils->free(name_without_realm.value); + if (name_token.value) + gss_release_buffer(&min_stat, &name_token); + if (without) + gss_release_name(&min_stat, &without); + SETERROR(text->utils, "GSSAPI Failure"); + sasl_gss_free_context_contents(text); + return SASL_BADAUTH; + } + + maj_stat = gss_compare_name(&min_stat, + text->client_name, + without, + &equal); + + if (GSS_ERROR(maj_stat)) { + params->utils->free(name_without_realm.value); + if (name_token.value) + gss_release_buffer(&min_stat, &name_token); + if (without) + gss_release_name(&min_stat, &without); + SETERROR(text->utils, "GSSAPI Failure"); + sasl_gss_free_context_contents(text); + return SASL_BADAUTH; + } + + gss_release_name(&min_stat,&without); + } else { + equal = 0; + } + + if (equal) { + text->authid = strdup(name_without_realm.value); + + if (text->authid == NULL) { + MEMERROR(params->utils); + return SASL_NOMEM; + } + } else { + text->authid = strdup(name_token.value); + + if (text->authid == NULL) { + MEMERROR(params->utils); + return SASL_NOMEM; + } + } +#else + { + ret = _plug_strdup(params->utils, name_token.value, + &text->authid, NULL); + } +#endif /* _SUN_SDK_ */ + + if (name_token.value) + gss_release_buffer(&min_stat, &name_token); + +#ifdef _SUN_SDK_ + if (ret != SASL_OK) + return (ret); +#else + if (name_without_realm.value) + params->utils->free(name_without_realm.value); +#endif /* _SUN_SDK_ */ + + + /* we have to decide what sort of encryption/integrity/etc., + we support */ + if (params->props.max_ssf < params->external_ssf) { + text->limitssf = 0; + } else { + text->limitssf = params->props.max_ssf - params->external_ssf; + } + if (params->props.min_ssf < params->external_ssf) { + text->requiressf = 0; + } else { + text->requiressf = params->props.min_ssf - params->external_ssf; + } + + /* build up our security properties token */ + if (params->props.maxbufsize > 0xFFFFFF) { + /* make sure maxbufsize isn't too large */ + /* maxbufsize = 0xFFFFFF */ + sasldata[1] = sasldata[2] = sasldata[3] = 0xFF; + } else { + sasldata[1] = (params->props.maxbufsize >> 16) & 0xFF; + sasldata[2] = (params->props.maxbufsize >> 8) & 0xFF; + sasldata[3] = (params->props.maxbufsize >> 0) & 0xFF; + } + sasldata[0] = 0; + if(text->requiressf != 0 && !params->props.maxbufsize) { +#ifdef _SUN_SDK_ + params->utils->log(params->utils->conn, SASL_LOG_ERR, + "GSSAPI needs a security layer but one is forbidden"); +#else + params->utils->seterror(params->utils->conn, 0, + "GSSAPI needs a security layer but one is forbidden"); +#endif /* _SUN_SDK_ */ + return SASL_TOOWEAK; + } + + if (text->requiressf == 0) { + sasldata[0] |= 1; /* authentication */ + } + if (text->requiressf <= 1 && text->limitssf >= 1 + && params->props.maxbufsize) { + sasldata[0] |= 2; + } + if (text->requiressf <= 56 && text->limitssf >= 56 + && params->props.maxbufsize) { + sasldata[0] |= 4; + } + + real_input_token.value = (void *)sasldata; + real_input_token.length = 4; + + maj_stat = gss_wrap(&min_stat, + text->gss_ctx, + 0, /* Just integrity checking here */ + GSS_C_QOP_DEFAULT, + input_token, + NULL, + output_token); + + if (GSS_ERROR(maj_stat)) { + sasl_gss_seterror(text->utils, maj_stat, min_stat); + if (output_token->value) + gss_release_buffer(&min_stat, output_token); + sasl_gss_free_context_contents(text); + return SASL_FAIL; + } + + + if (serveroutlen) + *serveroutlen = output_token->length; + if (output_token->value) { + if (serverout) { + ret = _plug_buf_alloc(text->utils, &(text->out_buf), + &(text->out_buf_len), *serveroutlen); + if(ret != SASL_OK) { + gss_release_buffer(&min_stat, output_token); + return ret; + } + memcpy(text->out_buf, output_token->value, *serveroutlen); + *serverout = text->out_buf; + } + + gss_release_buffer(&min_stat, output_token); + } + + /* Wait for ssf request and authid */ + text->state = SASL_GSSAPI_STATE_SSFREQ; + + return SASL_CONTINUE; + } + + case SASL_GSSAPI_STATE_SSFREQ: { + int layerchoice; + + real_input_token.value = (void *)clientin; + real_input_token.length = clientinlen; + + maj_stat = gss_unwrap(&min_stat, + text->gss_ctx, + input_token, + output_token, + NULL, + NULL); + + if (GSS_ERROR(maj_stat)) { + sasl_gss_seterror(text->utils, maj_stat, min_stat); + sasl_gss_free_context_contents(text); + return SASL_FAIL; + } + + layerchoice = (int)(((char *)(output_token->value))[0]); + if (layerchoice == 1 && text->requiressf == 0) { /* no encryption */ + oparams->encode = NULL; + oparams->decode = NULL; + oparams->mech_ssf = 0; + } else if (layerchoice == 2 && text->requiressf <= 1 && + text->limitssf >= 1) { /* integrity */ + oparams->encode=&gssapi_integrity_encode; + oparams->decode=&gssapi_decode; + oparams->mech_ssf=1; + } else if (layerchoice == 4 && text->requiressf <= 56 && + text->limitssf >= 56) { /* privacy */ + oparams->encode = &gssapi_privacy_encode; + oparams->decode = &gssapi_decode; + oparams->mech_ssf = 56; + } else { + /* not a supported encryption layer */ +#ifdef _SUN_SDK_ + text->utils->log(text->utils->conn, SASL_LOG_ERR, + "protocol violation: client requested invalid layer"); +#else + SETERROR(text->utils, + "protocol violation: client requested invalid layer"); +#endif /* _SUN_SDK_ */ + /* Mark that we attempted negotiation */ + oparams->mech_ssf = 2; + if (output_token->value) + gss_release_buffer(&min_stat, output_token); + sasl_gss_free_context_contents(text); + return SASL_FAIL; + } + + if (output_token->length > 4) { + int ret; + + ret = params->canon_user(params->utils->conn, + ((char *) output_token->value) + 4, + (output_token->length - 4) * sizeof(char), + SASL_CU_AUTHZID, oparams); + + if (ret != SASL_OK) { + sasl_gss_free_context_contents(text); + return ret; + } + + ret = params->canon_user(params->utils->conn, + text->authid, + 0, /* strlen(text->authid) */ + SASL_CU_AUTHID, oparams); + if (ret != SASL_OK) { + sasl_gss_free_context_contents(text); + return ret; + } + } else if(output_token->length == 4) { + /* null authzid */ + int ret; + + ret = params->canon_user(params->utils->conn, + text->authid, + 0, /* strlen(text->authid) */ + SASL_CU_AUTHZID | SASL_CU_AUTHID, + oparams); + + if (ret != SASL_OK) { + sasl_gss_free_context_contents(text); + return ret; + } + } else { +#ifdef _SUN_SDK_ + text->utils->log(text->utils->conn, SASL_LOG_ERR, + "token too short"); +#else + SETERROR(text->utils, + "token too short"); +#endif /* _SUN_SDK_ */ + gss_release_buffer(&min_stat, output_token); + sasl_gss_free_context_contents(text); + return SASL_FAIL; + } + + /* No matter what, set the rest of the oparams */ + oparams->maxoutbuf = + (((unsigned char *) output_token->value)[1] << 16) | + (((unsigned char *) output_token->value)[2] << 8) | + (((unsigned char *) output_token->value)[3] << 0); + +#ifdef _SUN_SDK_ + if (oparams->mech_ssf) { + oparams->maxoutbuf -= 4; /* Allow for 4 byte tag */ + maj_stat = gss_wrap_size_limit(&min_stat, + text->gss_ctx, + oparams->mech_ssf > 1, + GSS_C_QOP_DEFAULT, + oparams->maxoutbuf, + &max_input_size); + if (GSS_ERROR(maj_stat)) { + sasl_gss_seterror(text->utils, maj_stat, min_stat); + (void) gss_release_buffer(&min_stat, output_token); + sasl_gss_free_context_contents(text); + return (SASL_FAIL); + } + + /* + * gss_wrap_size_limit will return very big sizes for + * small input values + */ + if (max_input_size < oparams->maxoutbuf) + oparams->maxoutbuf = max_input_size; + else { + oparams->maxoutbuf = 0; + } + } +#else + if (oparams->mech_ssf) { + /* xxx this is probably too big */ + oparams->maxoutbuf -= 50; + } +#endif /* _SUN_SDK_ */ + + gss_release_buffer(&min_stat, output_token); + + text->state = SASL_GSSAPI_STATE_AUTHENTICATED; + + oparams->doneflag = 1; + + return SASL_OK; + } + + default: +#ifdef _SUN_SDK_ + params->utils->log(text->utils->conn, SASL_LOG_ERR, + "Invalid GSSAPI server step %d", text->state); +#else + params->utils->log(NULL, SASL_LOG_ERR, + "Invalid GSSAPI server step %d\n", text->state); +#endif /* _SUN_SDK_ */ + return SASL_FAIL; + } + +#ifndef _SUN_SDK_ + return SASL_FAIL; /* should never get here */ +#endif /* !_SUN_SDK_ */ +} + +#if defined _SUN_SDK_ && defined GSSAPI_PROTECT +static int +_gssapi_server_mech_step(void *conn_context, + sasl_server_params_t *params, + const char *clientin, + unsigned clientinlen, + const char **serverout, + unsigned *serveroutlen, + sasl_out_params_t *oparams) +{ + int ret; + + if (LOCK_MUTEX(&global_mutex) < 0) + return (SASL_FAIL); + + ret = gssapi_server_mech_step(conn_context, params, clientin, clientinlen, + serverout, serveroutlen, oparams); + + UNLOCK_MUTEX(&global_mutex); + return (ret); +} +#endif /* _SUN_SDK_ && GSSAPI_PROTECT */ + +static sasl_server_plug_t gssapi_server_plugins[] = +{ + { + "GSSAPI", /* mech_name */ + 56, /* max_ssf */ + SASL_SEC_NOPLAINTEXT + | SASL_SEC_NOACTIVE + | SASL_SEC_NOANONYMOUS + | SASL_SEC_MUTUAL_AUTH, /* security_flags */ + SASL_FEAT_WANT_CLIENT_FIRST + | SASL_FEAT_ALLOWS_PROXY, /* features */ + NULL, /* glob_context */ + &gssapi_server_mech_new, /* mech_new */ +#if defined _SUN_SDK_ && defined GSSAPI_PROTECT + &_gssapi_server_mech_step, /* mech_step */ +#else + &gssapi_server_mech_step, /* mech_step */ +#endif /* _SUN_SDK_ && GSSAPI_PROTECT */ + &gssapi_common_mech_dispose, /* mech_dispose */ + NULL, /* mech_free */ + NULL, /* setpass */ + NULL, /* user_query */ + NULL, /* idle */ + NULL, /* mech_avail */ + NULL /* spare */ + } +}; + +int gssapiv2_server_plug_init( +#ifndef HAVE_GSSKRB5_REGISTER_ACCEPTOR_IDENTITY + const sasl_utils_t *utils __attribute__((unused)), +#else + const sasl_utils_t *utils, +#endif + int maxversion, + int *out_version, + sasl_server_plug_t **pluglist, + int *plugcount) +{ +#ifdef HAVE_GSSKRB5_REGISTER_ACCEPTOR_IDENTITY + const char *keytab = NULL; + char keytab_path[1024]; + unsigned int rl; +#endif + + if (maxversion < SASL_SERVER_PLUG_VERSION) { + return SASL_BADVERS; + } + +#ifndef _SUN_SDK_ +#ifdef HAVE_GSSKRB5_REGISTER_ACCEPTOR_IDENTITY + /* unfortunately, we don't check for readability of keytab if it's + the standard one, since we don't know where it is */ + + /* FIXME: This code is broken */ + + utils->getopt(utils->getopt_context, "GSSAPI", "keytab", &keytab, &rl); + if (keytab != NULL) { + if (access(keytab, R_OK) != 0) { + utils->log(NULL, SASL_LOG_ERR, + "Could not find keytab file: %s: %m", + keytab, errno); + return SASL_FAIL; + } + + if(strlen(keytab) > 1024) { + utils->log(NULL, SASL_LOG_ERR, + "path to keytab is > 1024 characters"); + return SASL_BUFOVER; + } + + strncpy(keytab_path, keytab, 1024); + + gsskrb5_register_acceptor_identity(keytab_path); + } +#endif +#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. + */ + REG_PLUG("GSSAPI", gssapi_server_plugins); +#endif /* _INTEGRATED_SOLARIS_ */ + /* CRYPT DELETE END */ + /* EXPORT DELETE END */ + + *out_version = SASL_SERVER_PLUG_VERSION; + *pluglist = gssapi_server_plugins; + *plugcount = 1; + + return SASL_OK; +} + +/***************************** Client Section *****************************/ + +static int gssapi_client_mech_new(void *glob_context __attribute__((unused)), + sasl_client_params_t *params, + void **conn_context) +{ + context_t *text; +#ifdef _SUN_SDK_ + const char *use_authid = NULL; +#endif /* _SUN_SDK_ */ + + /* holds state are in */ +#if defined _SUN_SDK_ && defined GSSAPI_PROTECT + if (LOCK_MUTEX(&global_mutex) < 0) + return (SASL_FAIL); +#endif /* _SUN_SDK_ && GSSAPI_PROTECT */ + text = gss_new_context(params->utils); +#if defined _SUN_SDK_ && defined GSSAPI_PROTECT + UNLOCK_MUTEX(&global_mutex); +#endif /* _SUN_SDK_ && GSSAPI_PROTECT */ + if (text == NULL) { +#ifndef _SUN_SDK_ + MEMERROR(params->utils); +#endif /* !_SUN_SDK_ */ + return SASL_NOMEM; + } + + text->state = SASL_GSSAPI_STATE_AUTHNEG; + text->gss_ctx = GSS_C_NO_CONTEXT; + text->client_name = GSS_C_NO_NAME; + text->server_creds = GSS_C_NO_CREDENTIAL; + +#ifdef _SUN_SDK_ + params->utils->getopt(params->utils->getopt_context, + "GSSAPI", "use_authid", &use_authid, NULL); + text->use_authid = (use_authid != NULL) && + (*use_authid == 'y' || *use_authid == 'Y' || *use_authid == '1'); +#endif /* _SUN_SDK_ */ + + *conn_context = text; + + return SASL_OK; +} + +static int gssapi_client_mech_step(void *conn_context, + sasl_client_params_t *params, + const char *serverin, + unsigned serverinlen, + sasl_interact_t **prompt_need, + const char **clientout, + unsigned *clientoutlen, + sasl_out_params_t *oparams) +{ + context_t *text = (context_t *)conn_context; + gss_buffer_t input_token, output_token; + gss_buffer_desc real_input_token, real_output_token; + OM_uint32 maj_stat, min_stat; +#ifdef _SUN_SDK_ + OM_uint32 max_input_size; +#endif /* _SUN_SDK_ */ + gss_buffer_desc name_token; + int ret; + OM_uint32 req_flags, out_req_flags; + input_token = &real_input_token; + output_token = &real_output_token; + output_token->value = NULL; + input_token->value = NULL; + input_token->length = 0; + + *clientout = NULL; + *clientoutlen = 0; + + switch (text->state) { + + case SASL_GSSAPI_STATE_AUTHNEG: + /* try to get the userid */ +#ifdef _SUN_SDK_ + if (text->user == NULL || + (text->use_authid && text->client_authid == NULL)) { + int auth_result = SASL_OK; + int user_result = SASL_OK; + + if (text->use_authid && text->client_authid == NULL) { + auth_result = _plug_get_authid(params->utils, + &text->client_authid, + prompt_need); + + if ((auth_result != SASL_OK) && + (auth_result != SASL_INTERACT)) { + sasl_gss_free_context_contents(text); + return auth_result; + } + } + if (text->user == NULL) { + user_result = _plug_get_userid(params->utils, &text->user, + prompt_need); + + if ((user_result != SASL_OK) && + (user_result != SASL_INTERACT)) { + sasl_gss_free_context_contents(text); + return user_result; + } + } +#else + if (text->user == NULL) { + int user_result = SASL_OK; + + user_result = _plug_get_userid(params->utils, &text->user, + prompt_need); + + if ((user_result != SASL_OK) && (user_result != SASL_INTERACT)) { + sasl_gss_free_context_contents(text); + return user_result; + } +#endif /* _SUN_SDK_ */ + + /* free prompts we got */ + if (prompt_need && *prompt_need) { + params->utils->free(*prompt_need); + *prompt_need = NULL; + } + + /* if there are prompts not filled in */ +#ifdef _SUN_SDK_ + if ((user_result == SASL_INTERACT) || + (auth_result == SASL_INTERACT)) { + /* make the prompt list */ +#ifdef _INTEGRATED_SOLARIS_ + int result = _plug_make_prompts(params->utils, &text->h, + prompt_need, + user_result == SASL_INTERACT ? + convert_prompt(params->utils, &text->h, + gettext("Please enter your authorization name")) + : NULL, NULL, + auth_result == SASL_INTERACT ? + convert_prompt(params->utils, &text->h, + gettext("Please enter your authentication name")) + : NULL, NULL, + NULL, NULL, + NULL, NULL, NULL, + NULL, NULL, NULL); +#else + int result = _plug_make_prompts(params->utils, prompt_need, + user_result == SASL_INTERACT ? + "Please enter your authorization name" + : NULL, NULL, + auth_result == SASL_INTERACT ? + "Please enter your authentication name" + : NULL, NULL, + NULL, NULL, + NULL, NULL, NULL, + NULL, NULL, NULL); +#endif /* _INTEGRATED_SOLARIS_ */ + + if (result != SASL_OK) return result; + + return SASL_INTERACT; + } +#else + if (user_result == SASL_INTERACT) { + /* make the prompt list */ + int result = + _plug_make_prompts(params->utils, prompt_need, + user_result == SASL_INTERACT ? + "Please enter your authorization name" : NULL, NULL, + NULL, NULL, + NULL, NULL, + NULL, NULL, NULL, + NULL, NULL, NULL); + if (result != SASL_OK) return result; + + return SASL_INTERACT; + } +#endif /* _SUN_SDK_ */ + } + + if (text->server_name == GSS_C_NO_NAME) { /* only once */ + name_token.length = strlen(params->service) + 1 + strlen(params->serverFQDN); + name_token.value = (char *)params->utils->malloc((name_token.length + 1) * sizeof(char)); + if (name_token.value == NULL) { + sasl_gss_free_context_contents(text); + return SASL_NOMEM; + } + if (params->serverFQDN == NULL + || strlen(params->serverFQDN) == 0) { +#ifdef _SUN_SDK_ + text->utils->log(text->utils->conn, SASL_LOG_ERR, + "GSSAPI Failure: no serverFQDN"); +#else + SETERROR(text->utils, "GSSAPI Failure: no serverFQDN"); +#endif /* _SUN_SDK_ */ + return SASL_FAIL; + } + +#ifdef _SUN_SDK_ + snprintf(name_token.value, name_token.length + 1, + "%s@%s", params->service, params->serverFQDN); +#else + sprintf(name_token.value,"%s@%s", params->service, params->serverFQDN); +#endif /* _SUN_SDK_ */ + + maj_stat = gss_import_name (&min_stat, + &name_token, + GSS_C_NT_HOSTBASED_SERVICE, + &text->server_name); + + params->utils->free(name_token.value); + name_token.value = NULL; + + if (GSS_ERROR(maj_stat)) { + sasl_gss_seterror(text->utils, maj_stat, min_stat); + sasl_gss_free_context_contents(text); + return SASL_FAIL; + } + } + + if (serverinlen == 0) + input_token = GSS_C_NO_BUFFER; + + if (serverinlen) { + real_input_token.value = (void *)serverin; + real_input_token.length = serverinlen; + } + else if (text->gss_ctx != GSS_C_NO_CONTEXT ) { + /* This can't happen under GSSAPI: we have a non-null context + * and no input from the server. However, thanks to Imap, + * which discards our first output, this happens all the time. + * Throw away the context and try again. */ + maj_stat = gss_delete_sec_context (&min_stat,&text->gss_ctx,GSS_C_NO_BUFFER); + text->gss_ctx = GSS_C_NO_CONTEXT; + } + + /* Setup req_flags properly */ + req_flags = GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG; + if(params->props.max_ssf > params->external_ssf) { + /* We are requesting a security layer */ + req_flags |= GSS_C_INTEG_FLAG; + if(params->props.max_ssf - params->external_ssf > 56) { + /* We want to try for privacy */ + req_flags |= GSS_C_CONF_FLAG; + } + } + +#ifdef _SUN_SDK_ + if (text->use_authid && text->client_creds == GSS_C_NO_CREDENTIAL) { + gss_OID_set desired_mechs = GSS_C_NULL_OID_SET; + gss_buffer_desc name_token; + + name_token.length = strlen(text->client_authid); + name_token.value = (char *)text->client_authid; + + maj_stat = gss_import_name (&min_stat, + &name_token, +#ifdef HAVE_GSS_C_NT_USER_NAME + GSS_C_NT_USER_NAME, +#else + GSS_C_NULL_OID, +#endif + &text->client_name); + if (GSS_ERROR(maj_stat)) { + sasl_gss_seterror(text->utils, maj_stat, min_stat); + sasl_gss_free_context_contents(text); + return SASL_FAIL; + } + + if (text->mech_oid != GSS_C_NULL_OID) { + ret = add_mech_to_set(text, &desired_mechs); + if (ret != SASL_OK) + return (ret); + } + + maj_stat = gss_acquire_cred(&min_stat, + text->client_name, + GSS_C_INDEFINITE, + desired_mechs, + GSS_C_INITIATE, + &text->client_creds, + NULL, + NULL); + + if (desired_mechs != GSS_C_NULL_OID_SET) { + OM_uint32 min_stat2; + (void) gss_release_oid_set(&min_stat2, &desired_mechs); + } + + if (GSS_ERROR(maj_stat)) { + sasl_gss_seterror(text->utils, maj_stat, min_stat); + sasl_gss_free_context_contents(text); + return SASL_FAIL; + } + } +#endif /* _SUN_SDK_ */ + + maj_stat = gss_init_sec_context(&min_stat, +#ifdef _SUN_SDK_ + text->client_creds, +#else + GSS_C_NO_CREDENTIAL, +#endif /* _SUN_SDK_ */ + &text->gss_ctx, + text->server_name, +#ifdef _SUN_SDK_ + text->mech_oid, +#else + GSS_C_NO_OID, +#endif /* _SUN_SDK_ */ + req_flags, + 0, + GSS_C_NO_CHANNEL_BINDINGS, + input_token, + NULL, + output_token, + &out_req_flags, + NULL); + + if (GSS_ERROR(maj_stat)) { + sasl_gss_seterror(text->utils, maj_stat, min_stat); + if (output_token->value) + gss_release_buffer(&min_stat, output_token); + sasl_gss_free_context_contents(text); + return SASL_FAIL; + } + + *clientoutlen = output_token->length; + + if (output_token->value) { + if (clientout) { + ret = _plug_buf_alloc(text->utils, &(text->out_buf), + &(text->out_buf_len), *clientoutlen); + if(ret != SASL_OK) { + gss_release_buffer(&min_stat, output_token); + return ret; + } + memcpy(text->out_buf, output_token->value, *clientoutlen); + *clientout = text->out_buf; + } + + gss_release_buffer(&min_stat, output_token); + } + + if (maj_stat == GSS_S_COMPLETE) { + maj_stat = gss_inquire_context(&min_stat, + text->gss_ctx, + &text->client_name, + NULL, /* targ_name */ + NULL, /* lifetime */ + NULL, /* mech */ + NULL, /* flags */ + NULL, /* local init */ + NULL); /* open */ + + if (GSS_ERROR(maj_stat)) { + sasl_gss_seterror(text->utils, maj_stat, min_stat); + sasl_gss_free_context_contents(text); + return SASL_FAIL; + } + + name_token.length = 0; + maj_stat = gss_display_name(&min_stat, + text->client_name, + &name_token, + NULL); + + if (GSS_ERROR(maj_stat)) { + if (name_token.value) + gss_release_buffer(&min_stat, &name_token); +#ifdef _INTEGRATED_SOLARIS_ + SETERROR(text->utils, gettext("GSSAPI Failure")); +#else + SETERROR(text->utils, "GSSAPI Failure"); +#endif /* _INTEGRATED_SOLARIS_ */ + sasl_gss_free_context_contents(text); + return SASL_FAIL; + } + + if (text->user && text->user[0]) { + ret = params->canon_user(params->utils->conn, + text->user, 0, + SASL_CU_AUTHZID, oparams); + if (ret == SASL_OK) + ret = params->canon_user(params->utils->conn, + name_token.value, 0, + SASL_CU_AUTHID, oparams); + } else { + ret = params->canon_user(params->utils->conn, + name_token.value, 0, + SASL_CU_AUTHID | SASL_CU_AUTHZID, + oparams); + } + gss_release_buffer(&min_stat, &name_token); + + if (ret != SASL_OK) return ret; + + /* Switch to ssf negotiation */ + text->state = SASL_GSSAPI_STATE_SSFCAP; + } + + return SASL_CONTINUE; + + case SASL_GSSAPI_STATE_SSFCAP: { + sasl_security_properties_t *secprops = &(params->props); + unsigned int alen, external = params->external_ssf; + sasl_ssf_t need, allowed; + char serverhas, mychoice; + + real_input_token.value = (void *) serverin; + real_input_token.length = serverinlen; + + maj_stat = gss_unwrap(&min_stat, + text->gss_ctx, + input_token, + output_token, + NULL, + NULL); + + if (GSS_ERROR(maj_stat)) { + sasl_gss_seterror(text->utils, maj_stat, min_stat); + sasl_gss_free_context_contents(text); + if (output_token->value) + gss_release_buffer(&min_stat, output_token); + return SASL_FAIL; + } + + /* taken from kerberos.c */ + if (secprops->min_ssf > (56 + external)) { + return SASL_TOOWEAK; + } else if (secprops->min_ssf > secprops->max_ssf) { + return SASL_BADPARAM; + } + + /* need bits of layer -- sasl_ssf_t is unsigned so be careful */ + if (secprops->max_ssf >= external) { + allowed = secprops->max_ssf - external; + } else { + allowed = 0; + } + if (secprops->min_ssf >= external) { + need = secprops->min_ssf - external; + } else { + /* good to go */ + need = 0; + } + + /* bit mask of server support */ + serverhas = ((char *)output_token->value)[0]; + + /* if client didn't set use strongest layer available */ + if (allowed >= 56 && need <= 56 && (serverhas & 4)) { + /* encryption */ + oparams->encode = &gssapi_privacy_encode; + oparams->decode = &gssapi_decode; + oparams->mech_ssf = 56; + mychoice = 4; + } else if (allowed >= 1 && need <= 1 && (serverhas & 2)) { + /* integrity */ + oparams->encode = &gssapi_integrity_encode; + oparams->decode = &gssapi_decode; + oparams->mech_ssf = 1; + mychoice = 2; +#ifdef _SUN_SDK_ + } else if (need == 0 && (serverhas & 1)) { +#else + } else if (need <= 0 && (serverhas & 1)) { +#endif /* _SUN_SDK_ */ + /* no layer */ + oparams->encode = NULL; + oparams->decode = NULL; + oparams->mech_ssf = 0; + mychoice = 1; + } else { + /* there's no appropriate layering for us! */ + sasl_gss_free_context_contents(text); + return SASL_TOOWEAK; + } + + oparams->maxoutbuf = + (((unsigned char *) output_token->value)[1] << 16) | + (((unsigned char *) output_token->value)[2] << 8) | + (((unsigned char *) output_token->value)[3] << 0); + +#ifdef _SUN_SDK_ + if (oparams->mech_ssf > 0) { + oparams->maxoutbuf -= 4; /* Space for 4 byte length header */ + maj_stat = gss_wrap_size_limit(&min_stat, + text->gss_ctx, + oparams->mech_ssf > 1, + GSS_C_QOP_DEFAULT, + oparams->maxoutbuf, + &max_input_size); + if (GSS_ERROR(maj_stat)) { + sasl_gss_seterror(text->utils, maj_stat, min_stat); + (void) gss_release_buffer(&min_stat, output_token); + sasl_gss_free_context_contents(text); + return (SASL_FAIL); + } + + /* + * This is a workaround for a Solaris bug where + * gss_wrap_size_limit may return very big sizes for + * small input values + */ + if (max_input_size < oparams->maxoutbuf) + oparams->maxoutbuf = max_input_size; + else { + oparams->maxoutbuf = 0; + } + } +#else + if(oparams->mech_ssf) { + /* xxx probably too large */ + oparams->maxoutbuf -= 50; + } +#endif /* _SUN_SDK_ */ + + gss_release_buffer(&min_stat, output_token); + + /* oparams->user is always set, due to canon_user requirements. + * Make sure the client actually requested it though, by checking + * if our context was set. + */ + if (text->user && text->user[0]) + alen = strlen(oparams->user); + else + alen = 0; + + input_token->length = 4 + alen; + input_token->value = + (char *)params->utils->malloc((input_token->length + 1)*sizeof(char)); + if (input_token->value == NULL) { + sasl_gss_free_context_contents(text); + return SASL_NOMEM; + } + + if (alen) + memcpy((char *)input_token->value+4,oparams->user,alen); + + /* build up our security properties token */ + if (params->props.maxbufsize > 0xFFFFFF) { + /* make sure maxbufsize isn't too large */ + /* maxbufsize = 0xFFFFFF */ + ((unsigned char *)input_token->value)[1] = 0xFF; + ((unsigned char *)input_token->value)[2] = 0xFF; + ((unsigned char *)input_token->value)[3] = 0xFF; + } else { + ((unsigned char *)input_token->value)[1] = + (params->props.maxbufsize >> 16) & 0xFF; + ((unsigned char *)input_token->value)[2] = + (params->props.maxbufsize >> 8) & 0xFF; + ((unsigned char *)input_token->value)[3] = + (params->props.maxbufsize >> 0) & 0xFF; + } + ((unsigned char *)input_token->value)[0] = mychoice; + + maj_stat = gss_wrap (&min_stat, + text->gss_ctx, + 0, /* Just integrity checking here */ + GSS_C_QOP_DEFAULT, + input_token, + NULL, + output_token); + + params->utils->free(input_token->value); + input_token->value = NULL; + + if (GSS_ERROR(maj_stat)) { + sasl_gss_seterror(text->utils, maj_stat, min_stat); + if (output_token->value) + gss_release_buffer(&min_stat, output_token); + sasl_gss_free_context_contents(text); + return SASL_FAIL; + } + + if (clientoutlen) + *clientoutlen = output_token->length; + if (output_token->value) { + if (clientout) { + ret = _plug_buf_alloc(text->utils, &(text->out_buf), + &(text->out_buf_len), *clientoutlen); + if (ret != SASL_OK) { + gss_release_buffer(&min_stat, output_token); + return ret; + } + memcpy(text->out_buf, output_token->value, *clientoutlen); + *clientout = text->out_buf; + } + + gss_release_buffer(&min_stat, output_token); + } + + text->state = SASL_GSSAPI_STATE_AUTHENTICATED; + + oparams->doneflag = 1; + + return SASL_OK; + } + + default: +#ifdef _SUN_SDK_ + params->utils->log(params->utils->conn, SASL_LOG_ERR, + "Invalid GSSAPI client step %d", text->state); +#else + params->utils->log(NULL, SASL_LOG_ERR, + "Invalid GSSAPI client step %d\n", text->state); +#endif /* _SUN_SDK_ */ + return SASL_FAIL; + } + +#ifndef _SUN_SDK_ + return SASL_FAIL; /* should never get here */ +#endif /* !_SUN_SDK_ */ +} + +#ifdef _SUN_SDK_ +static const unsigned long gssapi_required_prompts[] = { +#else +static const long gssapi_required_prompts[] = { +#endif /* _SUN_SDK_ */ + SASL_CB_LIST_END +}; + +#if defined _SUN_SDK_ && defined GSSAPI_PROTECT +static int _gssapi_client_mech_step(void *conn_context, + sasl_client_params_t *params, + const char *serverin, + unsigned serverinlen, + sasl_interact_t **prompt_need, + const char **clientout, + unsigned *clientoutlen, + sasl_out_params_t *oparams) +{ + int ret; + + if (LOCK_MUTEX(&global_mutex) < 0) + return (SASL_FAIL); + + ret = gssapi_client_mech_step(conn_context, params, serverin, serverinlen, + prompt_need, clientout, clientoutlen, oparams); + + UNLOCK_MUTEX(&global_mutex); + return (ret); +} +#endif /* _SUN_SDK_ && GSSAPI_PROTECT */ + +static sasl_client_plug_t gssapi_client_plugins[] = +{ + { + "GSSAPI", /* mech_name */ + 56, /* max_ssf */ + SASL_SEC_NOPLAINTEXT + | SASL_SEC_NOACTIVE + | SASL_SEC_NOANONYMOUS + | SASL_SEC_MUTUAL_AUTH, /* security_flags */ + SASL_FEAT_WANT_CLIENT_FIRST + | SASL_FEAT_ALLOWS_PROXY, /* features */ + gssapi_required_prompts, /* required_prompts */ + NULL, /* glob_context */ + &gssapi_client_mech_new, /* mech_new */ +#if defined _SUN_SDK_ && defined GSSAPI_PROTECT + &_gssapi_client_mech_step, /* mech_step */ +#else + &gssapi_client_mech_step, /* mech_step */ +#endif /* _SUN_SDK_ && GSSAPI_PROTECT */ + &gssapi_common_mech_dispose, /* mech_dispose */ + NULL, /* mech_free */ + NULL, /* idle */ + NULL, /* spare */ + NULL /* spare */ + } +}; + +int gssapiv2_client_plug_init(const sasl_utils_t *utils __attribute__((unused)), + int maxversion, + int *out_version, + sasl_client_plug_t **pluglist, + int *plugcount) +{ + if (maxversion < SASL_CLIENT_PLUG_VERSION) { + SETERROR(utils, "Version mismatch in GSSAPI"); + return SASL_BADVERS; + } + + /* 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. + */ + REG_PLUG("GSSAPI", gssapi_client_plugins); +#endif /* _INTEGRATED_SOLARIS_ */ + /* CRYPT DELETE END */ + /* EXPORT DELETE END */ + + *out_version = SASL_CLIENT_PLUG_VERSION; + *pluglist = gssapi_client_plugins; + *plugcount = 1; + + return SASL_OK; +} diff --git a/usr/src/lib/sasl_plugins/gssapi/gssapiv2_init.c b/usr/src/lib/sasl_plugins/gssapi/gssapiv2_init.c new file mode 100644 index 0000000000..13d92bf75f --- /dev/null +++ b/usr/src/lib/sasl_plugins/gssapi/gssapiv2_init.c @@ -0,0 +1,44 @@ +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <config.h> + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#ifndef macintosh +#include <sys/stat.h> +#endif +#include <fcntl.h> +#include <assert.h> + +#include <sasl.h> +#include <saslplug.h> +#include <saslutil.h> + +#include "plugin_common.h" + +#ifdef macintosh +#include <sasl_gssapiv2_plugin_decl.h> +#endif + +#ifdef WIN32 +BOOL APIENTRY DllMain( HANDLE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved + ) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} +#endif + +SASL_CLIENT_PLUG_INIT( gssapiv2 ) +SASL_SERVER_PLUG_INIT( gssapiv2 ) + diff --git a/usr/src/lib/sasl_plugins/gssapi/i386/Makefile b/usr/src/lib/sasl_plugins/gssapi/i386/Makefile new file mode 100644 index 0000000000..ae4329fa74 --- /dev/null +++ b/usr/src/lib/sasl_plugins/gssapi/i386/Makefile @@ -0,0 +1,11 @@ +# +# Copyright 2003 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +# lib/libsasl/plugins/gssapi/i386/Makefile + +include ../Makefile.com + +install: all $(ROOTLIBS) diff --git a/usr/src/lib/sasl_plugins/gssapi/sparc/Makefile b/usr/src/lib/sasl_plugins/gssapi/sparc/Makefile new file mode 100644 index 0000000000..7f4953f857 --- /dev/null +++ b/usr/src/lib/sasl_plugins/gssapi/sparc/Makefile @@ -0,0 +1,11 @@ +# +# Copyright 2003 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +# lib/libsasl/plugins/gssapi/sparc/Makefile + +include ../Makefile.com + +install: all $(ROOTLIBS) diff --git a/usr/src/lib/sasl_plugins/gssapi/sparcv9/Makefile b/usr/src/lib/sasl_plugins/gssapi/sparcv9/Makefile new file mode 100644 index 0000000000..0188c0d9eb --- /dev/null +++ b/usr/src/lib/sasl_plugins/gssapi/sparcv9/Makefile @@ -0,0 +1,12 @@ +# +# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +# lib/libsasl/plugins/gssapi/sparcv9/Makefile + +include ../Makefile.com +include $(SRC)/lib/Makefile.lib.64 + +install: all $(ROOTLIBS64) diff --git a/usr/src/lib/sasl_plugins/gssapi/spec/Makefile b/usr/src/lib/sasl_plugins/gssapi/spec/Makefile new file mode 100644 index 0000000000..78e5a082a2 --- /dev/null +++ b/usr/src/lib/sasl_plugins/gssapi/spec/Makefile @@ -0,0 +1,7 @@ +# +# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" + +include $(SRC)/lib/Makefile.spec.arch diff --git a/usr/src/lib/sasl_plugins/gssapi/spec/Makefile.targ b/usr/src/lib/sasl_plugins/gssapi/spec/Makefile.targ new file mode 100644 index 0000000000..0093ca71ad --- /dev/null +++ b/usr/src/lib/sasl_plugins/gssapi/spec/Makefile.targ @@ -0,0 +1,10 @@ +# +# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" + +LIBRARY = gssapi.a +VERS = .1 + +OBJECTS = gssapi.o diff --git a/usr/src/lib/sasl_plugins/gssapi/spec/amd64/Makefile b/usr/src/lib/sasl_plugins/gssapi/spec/amd64/Makefile new file mode 100644 index 0000000000..07e64367c4 --- /dev/null +++ b/usr/src/lib/sasl_plugins/gssapi/spec/amd64/Makefile @@ -0,0 +1,14 @@ +# +# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +.KEEP_STATE: + +include ../Makefile.targ + +include $(SRC)/lib/Makefile.lib +include $(SRC)/lib/Makefile.lib.64 +include $(SRC)/lib/Makefile.spec diff --git a/usr/src/lib/sasl_plugins/gssapi/spec/gssapi.spec b/usr/src/lib/sasl_plugins/gssapi/spec/gssapi.spec new file mode 100644 index 0000000000..937b4fcf47 --- /dev/null +++ b/usr/src/lib/sasl_plugins/gssapi/spec/gssapi.spec @@ -0,0 +1,28 @@ +# +# Copyright 2003 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +# lib/libsasl/plugin/gssapi/spec/gssapi.spec +# + +function sasl_client_plug_init +include <sasl/saslplug.h> +declaration int sasl_client_plug_init(const sasl_utils_t *utils, \ + int maxversion, int *out_version, \ + sasl_client_plug_t **pluglist, \ + int *plugcount) +exception $return == SASL_FAIL +version SUNWprivate_1.1 +end + +function sasl_server_plug_init +include <sasl/saslplug.h> +declaration int sasl_server_plug_init(const sasl_utils_t *utils, \ + int maxversion, int *out_version, \ + sasl_server_plug_t **pluglist, \ + int *plugcount) +exception $return == SASL_FAIL +version SUNWprivate_1.1 +end diff --git a/usr/src/lib/sasl_plugins/gssapi/spec/i386/Makefile b/usr/src/lib/sasl_plugins/gssapi/spec/i386/Makefile new file mode 100644 index 0000000000..5d849b6467 --- /dev/null +++ b/usr/src/lib/sasl_plugins/gssapi/spec/i386/Makefile @@ -0,0 +1,15 @@ +# +# Copyright 2003 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# lib/libsasl/plugins/gssapi/spec/i386/Makefile +# + +.KEEP_STATE: + +include ../Makefile.targ + +include $(SRC)/lib/Makefile.lib +include $(SRC)/lib/Makefile.spec diff --git a/usr/src/lib/sasl_plugins/gssapi/spec/sparc/Makefile b/usr/src/lib/sasl_plugins/gssapi/spec/sparc/Makefile new file mode 100644 index 0000000000..0c9b8fa717 --- /dev/null +++ b/usr/src/lib/sasl_plugins/gssapi/spec/sparc/Makefile @@ -0,0 +1,15 @@ +# +# Copyright 2003 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# lib/libsasl/plugins/gssapi/spec/sparc/Makefile +# + +.KEEP_STATE: + +include ../Makefile.targ + +include $(SRC)/lib/Makefile.lib +include $(SRC)/lib/Makefile.spec diff --git a/usr/src/lib/sasl_plugins/gssapi/spec/sparcv9/Makefile b/usr/src/lib/sasl_plugins/gssapi/spec/sparcv9/Makefile new file mode 100644 index 0000000000..a7b2bf8e22 --- /dev/null +++ b/usr/src/lib/sasl_plugins/gssapi/spec/sparcv9/Makefile @@ -0,0 +1,16 @@ +# +# Copyright 2003 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# lib/libsasl/plugins/gssapi/spec/sparcv9/Makefile +# + +.KEEP_STATE: + +include ../Makefile.targ + +include $(SRC)/lib/Makefile.lib +include $(SRC)/lib/Makefile.lib.64 +include $(SRC)/lib/Makefile.spec diff --git a/usr/src/lib/sasl_plugins/gssapi/spec/versions b/usr/src/lib/sasl_plugins/gssapi/spec/versions new file mode 100644 index 0000000000..6a318d48a3 --- /dev/null +++ b/usr/src/lib/sasl_plugins/gssapi/spec/versions @@ -0,0 +1,19 @@ +# +# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +sparc { + SUNWprivate_1.1; +} +sparcv9 { + SUNWprivate_1.1; +} +i386 { + SUNWprivate_1.1; +} +amd64 { + SUNWprivate_1.1; +} diff --git a/usr/src/lib/sasl_plugins/plain/Makefile b/usr/src/lib/sasl_plugins/plain/Makefile new file mode 100644 index 0000000000..4897cf88f3 --- /dev/null +++ b/usr/src/lib/sasl_plugins/plain/Makefile @@ -0,0 +1,7 @@ +# +# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" + +include ../Makefile.subdirs diff --git a/usr/src/lib/sasl_plugins/plain/Makefile.com b/usr/src/lib/sasl_plugins/plain/Makefile.com new file mode 100644 index 0000000000..bd9358037c --- /dev/null +++ b/usr/src/lib/sasl_plugins/plain/Makefile.com @@ -0,0 +1,13 @@ +# +# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" + +LIBRARY= plain.a +VERS= .1 + +PLUG_OBJS= plain.o plain_init.o + +# include common definitions +include ../../Makefile.com diff --git a/usr/src/lib/sasl_plugins/plain/amd64/Makefile b/usr/src/lib/sasl_plugins/plain/amd64/Makefile new file mode 100644 index 0000000000..13ae0db12d --- /dev/null +++ b/usr/src/lib/sasl_plugins/plain/amd64/Makefile @@ -0,0 +1,11 @@ +# +# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +include ../Makefile.com +include ../../../Makefile.lib.64 + +install: all $(ROOTLIBS64) diff --git a/usr/src/lib/sasl_plugins/plain/i386/Makefile b/usr/src/lib/sasl_plugins/plain/i386/Makefile new file mode 100644 index 0000000000..661fac32f9 --- /dev/null +++ b/usr/src/lib/sasl_plugins/plain/i386/Makefile @@ -0,0 +1,11 @@ +# +# Copyright 2003 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +# lib/libsasl/plugins/plain/i386/Makefile + +include ../Makefile.com + +install: all $(ROOTLIBS) diff --git a/usr/src/lib/sasl_plugins/plain/plain.c b/usr/src/lib/sasl_plugins/plain/plain.c new file mode 100644 index 0000000000..97194dee5b --- /dev/null +++ b/usr/src/lib/sasl_plugins/plain/plain.c @@ -0,0 +1,514 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +#pragma ident "%Z%%M% %I% %E% SMI" + +/* Plain SASL plugin + * Rob Siemborski + * Tim Martin + * $Id: plain.c,v 1.61 2003/03/26 17:18:04 rjs3 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 + * the documentation and/or other materials provided with the + * 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 <stdio.h> +#include <string.h> +#include <sasl.h> +#include <saslplug.h> + +#include "plugin_common.h" + +#ifndef _SUN_SDK_ +#ifdef WIN32 +/* This must be after sasl.h */ +# include "saslPLAIN.h" +#endif /* WIN32 */ +#endif /* !_SUN_SDK_ */ + +#ifdef macintosh +#include <sasl_plain_plugin_decl.h> +#endif + +/***************************** Common Section *****************************/ + +#ifndef _SUN_SDK_ +static const char plugin_id[] = "$Id: plain.c,v 1.61 2003/03/26 17:18:04 rjs3 Exp $"; +#endif /* !_SUN_SDK_ */ + +/***************************** Server Section *****************************/ + +static int plain_server_mech_new(void *glob_context __attribute__((unused)), + sasl_server_params_t *sparams, + const char *challenge __attribute__((unused)), + unsigned challen __attribute__((unused)), + void **conn_context) +{ + /* holds state are in */ + if (!conn_context) { + PARAMERROR( sparams->utils ); + return SASL_BADPARAM; + } + + *conn_context = NULL; + + return SASL_OK; +} + +static int plain_server_mech_step(void *conn_context __attribute__((unused)), + sasl_server_params_t *params, + const char *clientin, + unsigned clientinlen, + const char **serverout, + unsigned *serveroutlen, + sasl_out_params_t *oparams) +{ + const char *author; + const char *authen; + const char *password; + size_t password_len; + unsigned lup=0; + int result; + char *passcopy; + + *serverout = NULL; + *serveroutlen = 0; + + /* should have received author-id NUL authen-id NUL password */ + + /* get author */ + author = clientin; + while ((lup < clientinlen) && (clientin[lup] != 0)) ++lup; + + if (lup >= clientinlen) { +#ifdef _SUN_SDK_ + params->utils->log(params->utils->conn, SASL_LOG_ERR, + "Can only find author (no password)"); +#else + SETERROR(params->utils, "Can only find author (no password)"); +#endif /* _SUN_SDK_ */ + return SASL_BADPROT; + } + + /* get authen */ + ++lup; + authen = clientin + lup; + while ((lup < clientinlen) && (clientin[lup] != 0)) ++lup; + + if (lup >= clientinlen) { +#ifdef _SUN_SDK_ + params->utils->log(params->utils->conn, SASL_LOG_ERR, + "Can only find author/en (no password)"); +#else + params->utils->seterror(params->utils->conn, 0, + "Can only find author/en (no password)"); +#endif /* _SUN_SDK_ */ + return SASL_BADPROT; + } + + /* get password */ + lup++; + password = clientin + lup; + while ((lup < clientinlen) && (clientin[lup] != 0)) ++lup; + + password_len = clientin + lup - password; + + if (lup != clientinlen) { +#ifdef _SUN_SDK_ + params->utils->log(params->utils->conn, SASL_LOG_ERR, + "Got more data than we were expecting in the PLAIN plugin"); +#else + SETERROR(params->utils, + "Got more data than we were expecting in the PLAIN plugin\n"); +#endif /* _SUN_SDK_ */ + return SASL_BADPROT; + } + + /* this kinda sucks. we need password to be null terminated + but we can't assume there is an allocated byte at the end + of password so we have to copy it */ + passcopy = params->utils->malloc(password_len + 1); + if (passcopy == NULL) { + MEMERROR(params->utils); + return SASL_NOMEM; + } + + strncpy(passcopy, password, password_len); + passcopy[password_len] = '\0'; + + /* Canonicalize userid first, so that password verification is only + * against the canonical id */ + if (!author || !*author) + author = authen; + + result = params->canon_user(params->utils->conn, + authen, 0, SASL_CU_AUTHID, oparams); + if (result != SASL_OK) { + _plug_free_string(params->utils, &passcopy); + return result; + } + + /* verify password - return sasl_ok on success*/ + result = params->utils->checkpass(params->utils->conn, + oparams->authid, oparams->alen, + passcopy, password_len); + + _plug_free_string(params->utils, &passcopy); + + if (result != SASL_OK) { +#ifdef _INTEGRATED_SOLARIS_ + params->utils->seterror(params->utils->conn, 0, + gettext("Password verification failed")); +#else + params->utils->seterror(params->utils->conn, 0, + "Password verification failed"); +#endif /* _INTEGRATED_SOLARIS_ */ + return result; + } + + /* Canonicalize and store the authorization ID */ + /* We need to do this after calling verify_user just in case verify_user + * needed to get auxprops itself */ + result = params->canon_user(params->utils->conn, + author, 0, SASL_CU_AUTHZID, oparams); + if (result != SASL_OK) return result; + + /* Transition? */ + if (params->transition) { + params->transition(params->utils->conn, password, password_len); + } + + /* set oparams */ + oparams->doneflag = 1; + oparams->mech_ssf = 0; + oparams->maxoutbuf = 0; + oparams->encode_context = NULL; + oparams->encode = NULL; + oparams->decode_context = NULL; + oparams->decode = NULL; + oparams->param_version = 0; + + return SASL_OK; +} + +static sasl_server_plug_t plain_server_plugins[] = +{ + { + "PLAIN", /* mech_name */ + 0, /* max_ssf */ + SASL_SEC_NOANONYMOUS, /* security_flags */ + SASL_FEAT_WANT_CLIENT_FIRST + | SASL_FEAT_ALLOWS_PROXY, /* features */ + NULL, /* glob_context */ + &plain_server_mech_new, /* mech_new */ + &plain_server_mech_step, /* mech_step */ + NULL, /* mech_dispose */ + NULL, /* mech_free */ + NULL, /* setpass */ + NULL, /* user_query */ + NULL, /* idle */ + NULL, /* mech_avail */ + NULL /* spare */ + } +}; + +int plain_server_plug_init(const sasl_utils_t *utils, + int maxversion, + int *out_version, + sasl_server_plug_t **pluglist, + int *plugcount) +{ + if (maxversion < SASL_SERVER_PLUG_VERSION) { + SETERROR(utils, "PLAIN version mismatch"); + return SASL_BADVERS; + } + + *out_version = SASL_SERVER_PLUG_VERSION; + *pluglist = plain_server_plugins; + *plugcount = 1; + + return SASL_OK; +} + +/***************************** Client Section *****************************/ + +typedef struct client_context { + char *out_buf; + unsigned out_buf_len; +#ifdef _INTEGRATED_SOLARIS_ + void *h; +#endif /* _INTEGRATED_SOLARIS_ */ +} client_context_t; + +static int plain_client_mech_new(void *glob_context __attribute__((unused)), + sasl_client_params_t *params, + void **conn_context) +{ + client_context_t *text; + + /* holds state are in */ + text = params->utils->malloc(sizeof(client_context_t)); + if (text == NULL) { + MEMERROR( params->utils ); + return SASL_NOMEM; + } + + memset(text, 0, sizeof(client_context_t)); + + *conn_context = text; + + return SASL_OK; +} + +static int plain_client_mech_step(void *conn_context, + sasl_client_params_t *params, + const char *serverin __attribute__((unused)), + unsigned serverinlen __attribute__((unused)), + sasl_interact_t **prompt_need, + const char **clientout, + unsigned *clientoutlen, + sasl_out_params_t *oparams) +{ + client_context_t *text = (client_context_t *) conn_context; + const char *user = NULL, *authid = NULL; + sasl_secret_t *password = NULL; + unsigned int free_password = 0; /* set if we need to free password */ + int user_result = SASL_OK; + int auth_result = SASL_OK; + int pass_result = SASL_OK; + int result; + + *clientout = NULL; + *clientoutlen = 0; + + /* doesn't really matter how the server responds */ + + /* check if sec layer strong enough */ + if (params->props.min_ssf > params->external_ssf) { +#ifdef _INTEGRATED_SOLARIS_ + SETERROR( params->utils, gettext("SSF requested of PLAIN plugin")); +#else + SETERROR( params->utils, "SSF requested of PLAIN plugin"); +#endif /* _INTEGRATED_SOLARIS_ */ + return SASL_TOOWEAK; + } + + /* try to get the authid */ + if (oparams->authid == NULL) { + auth_result = _plug_get_authid(params->utils, &authid, prompt_need); + + if ((auth_result != SASL_OK) && (auth_result != SASL_INTERACT)) + return auth_result; + } + + /* try to get the userid */ + if (oparams->user == NULL) { + user_result = _plug_get_userid(params->utils, &user, prompt_need); + + if ((user_result != SASL_OK) && (user_result != SASL_INTERACT)) + return user_result; + } + + /* try to get the password */ + if (password == NULL) { + pass_result = _plug_get_password(params->utils, &password, + &free_password, prompt_need); + + if ((pass_result != SASL_OK) && (pass_result != SASL_INTERACT)) + return pass_result; + } + + /* free prompts we got */ + if (prompt_need && *prompt_need) { + params->utils->free(*prompt_need); + *prompt_need = NULL; + } + + /* if there are prompts not filled in */ + if ((user_result == SASL_INTERACT) || (auth_result == SASL_INTERACT) || + (pass_result == SASL_INTERACT)) { + /* make the prompt list */ + result = +#ifdef _INTEGRATED_SOLARIS_ + _plug_make_prompts(params->utils, &text->h, prompt_need, + user_result == SASL_INTERACT ? + convert_prompt(params->utils, &text->h, + gettext("Please enter your authorization name")) + : NULL, + NULL, + auth_result == SASL_INTERACT ? + convert_prompt(params->utils, &text->h, + gettext("Please enter your authentication name")) + : NULL, + NULL, + pass_result == SASL_INTERACT ? + convert_prompt(params->utils, &text->h, + gettext("Please enter your password")) : NULL, + NULL, + NULL, NULL, NULL, + NULL, NULL, NULL); +#else + _plug_make_prompts(params->utils, prompt_need, + 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 password" : NULL, NULL, + NULL, NULL, NULL, + NULL, NULL, NULL); +#endif /* _INTEGRATED_SOLARIS_ */ + if (result != SASL_OK) goto cleanup; + + return SASL_INTERACT; + } + + if (!password) { + PARAMERROR(params->utils); + return SASL_BADPARAM; + } + + if (!user || !*user) { + result = params->canon_user(params->utils->conn, authid, 0, + SASL_CU_AUTHID | SASL_CU_AUTHZID, oparams); + } + else { + result = params->canon_user(params->utils->conn, user, 0, + SASL_CU_AUTHZID, oparams); + if (result != SASL_OK) goto cleanup; + + result = params->canon_user(params->utils->conn, authid, 0, + SASL_CU_AUTHID, oparams); + } + if (result != SASL_OK) goto cleanup; + + /* send authorized id NUL authentication id NUL password */ + *clientoutlen = (oparams->ulen + 1 + + oparams->alen + 1 + + password->len); + + /* remember the extra NUL on the end for stupid clients */ + result = _plug_buf_alloc(params->utils, &(text->out_buf), + &(text->out_buf_len), *clientoutlen + 1); + if (result != SASL_OK) goto cleanup; + + memset(text->out_buf, 0, *clientoutlen + 1); + memcpy(text->out_buf, oparams->user, oparams->ulen); + memcpy(text->out_buf + oparams->ulen + 1, oparams->authid, oparams->alen); + memcpy(text->out_buf + oparams->ulen + oparams->alen + 2, + password->data, password->len); + + *clientout = text->out_buf; + + /* set oparams */ + oparams->doneflag = 1; + oparams->mech_ssf = 0; + oparams->maxoutbuf = 0; + oparams->encode_context = NULL; + oparams->encode = NULL; + oparams->decode_context = NULL; + oparams->decode = NULL; + oparams->param_version = 0; + + result = SASL_OK; + + cleanup: + /* free sensitive info */ + if (free_password) _plug_free_secret(params->utils, &password); + + return result; +} + +static void plain_client_mech_dispose(void *conn_context, + const sasl_utils_t *utils) +{ + client_context_t *text = (client_context_t *) conn_context; + + if (!text) return; + + if (text->out_buf) utils->free(text->out_buf); +#ifdef _INTEGRATED_SOLARIS_ + convert_prompt(utils, &text->h, NULL); +#endif /* _INTEGRATED_SOLARIS_ */ + + utils->free(text); +} + +static sasl_client_plug_t plain_client_plugins[] = +{ + { + "PLAIN", /* mech_name */ + 0, /* max_ssf */ + SASL_SEC_NOANONYMOUS, /* security_flags */ + SASL_FEAT_WANT_CLIENT_FIRST + | SASL_FEAT_ALLOWS_PROXY, /* features */ + NULL, /* required_prompts */ + NULL, /* glob_context */ + &plain_client_mech_new, /* mech_new */ + &plain_client_mech_step, /* mech_step */ + &plain_client_mech_dispose, /* mech_dispose */ + NULL, /* mech_free */ + NULL, /* idle */ + NULL, /* spare */ + NULL /* spare */ + } +}; + +int plain_client_plug_init(sasl_utils_t *utils, + int maxversion, + int *out_version, + sasl_client_plug_t **pluglist, + int *plugcount) +{ + if (maxversion < SASL_CLIENT_PLUG_VERSION) { + SETERROR(utils, "PLAIN version mismatch"); + return SASL_BADVERS; + } + + *out_version = SASL_CLIENT_PLUG_VERSION; + *pluglist = plain_client_plugins; + *plugcount = 1; + + return SASL_OK; +} diff --git a/usr/src/lib/sasl_plugins/plain/plain_init.c b/usr/src/lib/sasl_plugins/plain/plain_init.c new file mode 100644 index 0000000000..90c931f6e9 --- /dev/null +++ b/usr/src/lib/sasl_plugins/plain/plain_init.c @@ -0,0 +1,44 @@ +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <config.h> + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#ifndef macintosh +#include <sys/stat.h> +#endif +#include <fcntl.h> +#include <assert.h> + +#include <sasl.h> +#include <saslplug.h> +#include <saslutil.h> + +#include "plugin_common.h" + +#ifdef macintosh +#include <sasl_plain_plugin_decl.h> +#endif + +#ifdef WIN32 +BOOL APIENTRY DllMain( HANDLE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved + ) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} +#endif + +SASL_CLIENT_PLUG_INIT( plain ) +SASL_SERVER_PLUG_INIT( plain ) + diff --git a/usr/src/lib/sasl_plugins/plain/sparc/Makefile b/usr/src/lib/sasl_plugins/plain/sparc/Makefile new file mode 100644 index 0000000000..32efb790e3 --- /dev/null +++ b/usr/src/lib/sasl_plugins/plain/sparc/Makefile @@ -0,0 +1,11 @@ +# +# Copyright 2003 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +# lib/libsasl/plugins/login/sparc/Makefile + +include ../Makefile.com + +install: all $(ROOTLIBS) diff --git a/usr/src/lib/sasl_plugins/plain/sparcv9/Makefile b/usr/src/lib/sasl_plugins/plain/sparcv9/Makefile new file mode 100644 index 0000000000..31beb8be62 --- /dev/null +++ b/usr/src/lib/sasl_plugins/plain/sparcv9/Makefile @@ -0,0 +1,12 @@ +# +# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +# lib/libsasl/plugins/login/sparcv9/Makefile + +include ../Makefile.com +include $(SRC)/lib/Makefile.lib.64 + +install: all $(ROOTLIBS64) diff --git a/usr/src/lib/sasl_plugins/plain/spec/Makefile b/usr/src/lib/sasl_plugins/plain/spec/Makefile new file mode 100644 index 0000000000..78e5a082a2 --- /dev/null +++ b/usr/src/lib/sasl_plugins/plain/spec/Makefile @@ -0,0 +1,7 @@ +# +# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" + +include $(SRC)/lib/Makefile.spec.arch diff --git a/usr/src/lib/sasl_plugins/plain/spec/Makefile.targ b/usr/src/lib/sasl_plugins/plain/spec/Makefile.targ new file mode 100644 index 0000000000..340888b209 --- /dev/null +++ b/usr/src/lib/sasl_plugins/plain/spec/Makefile.targ @@ -0,0 +1,10 @@ +# +# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" + +LIBRARY = plain.a +VERS = .1 + +OBJECTS = plain.o diff --git a/usr/src/lib/sasl_plugins/plain/spec/amd64/Makefile b/usr/src/lib/sasl_plugins/plain/spec/amd64/Makefile new file mode 100644 index 0000000000..07e64367c4 --- /dev/null +++ b/usr/src/lib/sasl_plugins/plain/spec/amd64/Makefile @@ -0,0 +1,14 @@ +# +# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +.KEEP_STATE: + +include ../Makefile.targ + +include $(SRC)/lib/Makefile.lib +include $(SRC)/lib/Makefile.lib.64 +include $(SRC)/lib/Makefile.spec diff --git a/usr/src/lib/sasl_plugins/plain/spec/i386/Makefile b/usr/src/lib/sasl_plugins/plain/spec/i386/Makefile new file mode 100644 index 0000000000..b15851e8d3 --- /dev/null +++ b/usr/src/lib/sasl_plugins/plain/spec/i386/Makefile @@ -0,0 +1,15 @@ +# +# Copyright 2003 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# lib/libsasl/plugins/plain/spec/i386/Makefile +# + +.KEEP_STATE: + +include ../Makefile.targ + +include $(SRC)/lib/Makefile.lib +include $(SRC)/lib/Makefile.spec diff --git a/usr/src/lib/sasl_plugins/plain/spec/plain.spec b/usr/src/lib/sasl_plugins/plain/spec/plain.spec new file mode 100644 index 0000000000..8f6fc71926 --- /dev/null +++ b/usr/src/lib/sasl_plugins/plain/spec/plain.spec @@ -0,0 +1,28 @@ +# +# Copyright 2003 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +# lib/libsasl/plugin/login/spec/login.spec +# + +function sasl_client_plug_init +include <sasl/saslplug.h> +declaration int sasl_client_plug_init(const sasl_utils_t *utils, \ + int maxversion, int *out_version, \ + sasl_client_plug_t **pluglist, \ + int *plugcount) +exception $return == SASL_FAIL +version SUNWprivate_1.1 +end + +function sasl_server_plug_init +include <sasl/saslplug.h> +declaration int sasl_server_plug_init(const sasl_utils_t *utils, \ + int maxversion, int *out_version, \ + sasl_server_plug_t **pluglist, \ + int *plugcount) +exception $return == SASL_FAIL +version SUNWprivate_1.1 +end diff --git a/usr/src/lib/sasl_plugins/plain/spec/sparc/Makefile b/usr/src/lib/sasl_plugins/plain/spec/sparc/Makefile new file mode 100644 index 0000000000..8fdc1965c6 --- /dev/null +++ b/usr/src/lib/sasl_plugins/plain/spec/sparc/Makefile @@ -0,0 +1,15 @@ +# +# Copyright 2003 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# lib/libsasl/plugins/plain/spec/sparc/Makefile +# + +.KEEP_STATE: + +include ../Makefile.targ + +include $(SRC)/lib/Makefile.lib +include $(SRC)/lib/Makefile.spec diff --git a/usr/src/lib/sasl_plugins/plain/spec/sparcv9/Makefile b/usr/src/lib/sasl_plugins/plain/spec/sparcv9/Makefile new file mode 100644 index 0000000000..b000497abc --- /dev/null +++ b/usr/src/lib/sasl_plugins/plain/spec/sparcv9/Makefile @@ -0,0 +1,16 @@ +# +# Copyright 2003 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# lib/libsasl/plugins/plain/spec/sparcv9/Makefile +# + +.KEEP_STATE: + +include ../Makefile.targ + +include $(SRC)/lib/Makefile.lib +include $(SRC)/lib/Makefile.lib.64 +include $(SRC)/lib/Makefile.spec diff --git a/usr/src/lib/sasl_plugins/plain/spec/versions b/usr/src/lib/sasl_plugins/plain/spec/versions new file mode 100644 index 0000000000..6a318d48a3 --- /dev/null +++ b/usr/src/lib/sasl_plugins/plain/spec/versions @@ -0,0 +1,19 @@ +# +# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +sparc { + SUNWprivate_1.1; +} +sparcv9 { + SUNWprivate_1.1; +} +i386 { + SUNWprivate_1.1; +} +amd64 { + SUNWprivate_1.1; +} |
