1 /* Kerberos4 SASL plugin
4 * $Id: kerberos4.c,v 1.99 2005/01/10 07:08:53 shadow Exp $
7 * Copyright (c) 1998-2003 Carnegie Mellon University. All rights reserved.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in
18 * the documentation and/or other materials provided with the
21 * 3. The name "Carnegie Mellon University" must not be used to
22 * endorse or promote products derived from this software without
23 * prior written permission. For permission or any other legal
24 * details, please contact
25 * Office of Technology Transfer
26 * Carnegie Mellon University
28 * Pittsburgh, PA 15213-3890
29 * (412) 268-4387, fax: (412) 268-7395
30 * tech-transfer@andrew.cmu.edu
32 * 4. Redistributions of any form whatsoever must retain the following
34 * "This product includes software developed by Computing Services
35 * at Carnegie Mellon University (http://www.cmu.edu/computing/)."
37 * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
38 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
39 * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
40 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
41 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
42 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
43 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
53 # include <openssl/des.h>
56 # endif /* WITH_SSL_DES */
60 # include <winsock2.h>
61 #elif defined(macintosh)
62 #include <kcglue_krb.h>
64 # include <sys/param.h>
65 # include <sys/socket.h>
66 # include <netinet/in.h>
67 # include <arpa/inet.h>
81 #include "plugin_common.h"
85 * krb.h doenst include some functions and mac compiler is picky
88 #include <extra_krb.h>
89 #include <sasl_kerberos4_plugin_decl.h>
93 /* This must be after sasl.h, saslutil.h */
94 # include "saslKERBEROSV4.h"
96 /* KClient doesn't define this */
97 typedef struct krb_principal {
99 char instance[INST_SZ];
100 char realm[REALM_SZ];
103 /* This isn't defined under WIN32. For access() */
107 /* we also need io.h for access() prototype */
112 /* gotta define gethostname ourselves on suns */
113 extern int gethostname(char *, int);
116 /***************************** Common Section *****************************/
118 static const char plugin_id[] = "$Id: kerberos4.c,v 1.99 2005/01/10 07:08:53 shadow Exp $";
121 #define KEYFILE "/etc/srvtab";
124 #define KRB_SECFLAG_NONE (1)
125 #define KRB_SECFLAG_INTEGRITY (2)
126 #define KRB_SECFLAG_ENCRYPTION (4)
127 #define KRB_SECFLAGS (7)
128 #define KRB_SECFLAG_CREDENTIALS (8)
130 #define KRB_DES_SECURITY_BITS (56)
131 #define KRB_INTEGRITY_BITS (1)
133 typedef enum Krb_sec {
135 KRB_SEC_INTEGRITY = 1,
136 KRB_SEC_ENCRYPTION = 2
139 typedef struct context {
142 int challenge; /* this is the challenge (32 bit int) used
143 for the authentication */
145 char *service; /* kerberos service */
146 char instance[ANAME_SZ];
147 char pname[ANAME_SZ];
149 char prealm[REALM_SZ];
150 char *hostname; /* hostname */
151 char *realm; /* kerberos realm */
154 CREDENTIALS credentials;
156 des_cblock key; /* session key */
157 des_cblock session; /* session key */
159 des_key_schedule init_keysched; /* key schedule for initialization */
160 des_key_schedule enc_keysched; /* encryption key schedule */
161 des_key_schedule dec_keysched; /* decryption key schedule */
164 struct sockaddr_in ip_local; /* local ip address and port.
166 struct sockaddr_in ip_remote; /* remote ip address and port.
169 const sasl_utils_t *utils; /* this is useful to have around */
172 char *encode_buf; /* For encoding/decoding mem management */
174 char *decode_once_buf;
175 unsigned encode_buf_len;
176 unsigned decode_buf_len;
177 unsigned decode_once_buf_len;
178 buffer_info_t *enc_in_buf;
180 decode_context_t decode_context;
182 char *out_buf; /* per-step mem management */
183 unsigned out_buf_len;
185 const char *user; /* used by client */
187 int secflags; /* client/server supports layers? */
189 long time_sec; /* These are used to make sure we are getting */
190 char time_5ms; /* strictly increasing timestamps */
194 #define KRB_LOCK_MUTEX(utils) \
195 if(((sasl_utils_t *)(utils))->mutex_lock(krb_mutex) != 0) { \
196 ((sasl_utils_t *)(utils))->seterror(((sasl_utils_t *)(utils))->conn, \
197 0, "error locking mutex"); \
200 #define KRB_UNLOCK_MUTEX(utils) \
201 if(((sasl_utils_t *)(utils))->mutex_unlock(krb_mutex) != 0) { \
202 ((sasl_utils_t *)(utils))->seterror(((sasl_utils_t *)(utils))->conn, \
203 0, "error unlocking mutex"); \
207 /* Mutex for not-thread-safe kerberos 4 library */
208 static void *krb_mutex = NULL;
209 static char *srvtab = NULL;
210 static unsigned refcount = 0;
212 static int kerberosv4_encode(void *context,
213 const struct iovec *invec,
219 context_t *text = (context_t *)context;
220 struct buffer_info *inblob, bufinfo;
223 ret = _plug_iovec_to_buf(text->utils, invec, numiov, &text->enc_in_buf);
224 if(ret != SASL_OK) return ret;
225 inblob = text->enc_in_buf;
227 bufinfo.data = invec[0].iov_base;
228 bufinfo.curlen = invec[0].iov_len;
232 ret = _plug_buf_alloc(text->utils, &(text->encode_buf),
233 &text->encode_buf_len, inblob->curlen+40);
235 if(ret != SASL_OK) return ret;
237 KRB_LOCK_MUTEX(text->utils);
239 if (text->sec_type == KRB_SEC_ENCRYPTION) {
240 /* Type incompatibility on 4th arg probably means you're
241 building against krb4 in MIT krb5, but got the OpenSSL
242 headers in your way. You need to not use openssl/des.h with
244 len=krb_mk_priv(inblob->data, (text->encode_buf+4),
245 inblob->curlen, text->init_keysched,
246 &text->session, &text->ip_local,
248 } else if (text->sec_type == KRB_SEC_INTEGRITY) {
249 len=krb_mk_safe(inblob->data, (text->encode_buf+4),
251 &text->session, &text->ip_local, &text->ip_remote);
256 KRB_UNLOCK_MUTEX(text->utils);
258 /* returns -1 on error */
259 if (len==-1) return SASL_FAIL;
261 /* now copy in the len of the buffer in network byte order */
264 memcpy(text->encode_buf, &len, 4);
266 /* Setup the const pointer */
267 *output = text->encode_buf;
272 static int kerberosv4_decode_packet(void *context,
273 const char *input, unsigned inputlen,
274 char **output, unsigned *outputlen)
276 context_t *text = (context_t *) context;
280 memset(&data,0,sizeof(MSG_DAT));
282 KRB_LOCK_MUTEX(text->utils);
284 if (text->sec_type == KRB_SEC_ENCRYPTION) {
285 result=krb_rd_priv(input, inputlen, text->init_keysched,
286 &text->session, &text->ip_remote,
287 &text->ip_local, &data);
288 } else if (text->sec_type == KRB_SEC_INTEGRITY) {
289 result = krb_rd_safe(input, inputlen,
290 &text->session, &text->ip_remote,
291 &text->ip_local, &data);
293 KRB_UNLOCK_MUTEX(text->utils);
294 text->utils->seterror(text->utils->conn, 0,
295 "KERBEROS_4 decode called with KRB_SEC_NONE");
299 KRB_UNLOCK_MUTEX(text->utils);
301 /* see if the krb library gave us a failure */
303 text->utils->seterror(text->utils->conn, 0, get_krb_err_txt(result));
307 /* check to make sure the timestamps are ok */
308 if ((data.time_sec < text->time_sec) || /* if an earlier time */
309 (((data.time_sec == text->time_sec) && /* or the exact same time */
310 (data.time_5ms < text->time_5ms))))
312 text->utils->seterror(text->utils->conn, 0, "timestamps not ok");
316 text->time_sec = data.time_sec;
317 text->time_5ms = data.time_5ms;
319 result = _plug_buf_alloc(text->utils, &text->decode_once_buf,
320 &text->decode_once_buf_len,
321 data.app_length + 1);
322 if(result != SASL_OK)
325 *output = text->decode_once_buf;
326 *outputlen = data.app_length;
327 memcpy(*output, data.app_data, data.app_length);
328 (*output)[*outputlen] = '\0';
333 static int kerberosv4_decode(void *context,
334 const char *input, unsigned inputlen,
335 const char **output, unsigned *outputlen)
337 context_t *text = (context_t *) context;
340 ret = _plug_decode(&text->decode_context, input, inputlen,
341 &text->decode_buf, &text->decode_buf_len, outputlen,
342 kerberosv4_decode_packet, text);
344 *output = text->decode_buf;
349 static int new_text(const sasl_utils_t *utils, context_t **text)
351 context_t *ret = (context_t *) utils->malloc(sizeof(context_t));
358 memset(ret, 0, sizeof(context_t));
368 static void kerberosv4_common_mech_dispose(void *conn_context,
369 const sasl_utils_t *utils)
371 context_t *text = (context_t *)conn_context;
375 _plug_decode_free(&text->decode_context);
376 if (text->encode_buf) utils->free(text->encode_buf);
377 if (text->decode_buf) utils->free(text->decode_buf);
378 if (text->decode_once_buf) utils->free(text->decode_once_buf);
379 if (text->out_buf) utils->free(text->out_buf);
380 if (text->enc_in_buf) {
381 if(text->enc_in_buf->data) utils->free(text->enc_in_buf->data);
382 utils->free(text->enc_in_buf);
384 /* no need to free userid, it's just the interaction result */
390 kerberosv4_common_mech_free(void *glob_context __attribute__((unused)),
391 const sasl_utils_t *utils)
394 utils->mutex_free(krb_mutex);
395 krb_mutex = NULL; /* in case we need to re-use it */
398 if (srvtab && !refcount) {
404 /***************************** Server Section *****************************/
406 static int cando_sec(sasl_security_properties_t *props,
413 if(props->maxbufsize == 0) {
416 need = props->max_ssf - external_ssf;
417 musthave = props->min_ssf - external_ssf;
421 case KRB_SECFLAG_NONE:
425 case KRB_SECFLAG_INTEGRITY:
426 if ((musthave <= KRB_INTEGRITY_BITS)
427 && (KRB_INTEGRITY_BITS <= need))
430 case KRB_SECFLAG_ENCRYPTION:
431 if ((musthave <= KRB_DES_SECURITY_BITS)
432 && (KRB_DES_SECURITY_BITS <= need))
435 case KRB_SECFLAG_CREDENTIALS:
436 if (props->security_flags & SASL_SEC_PASS_CREDENTIALS)
443 static int ipv4_ipfromstring(const sasl_utils_t *utils, const char *addr,
444 struct sockaddr_in *out)
446 struct sockaddr_storage ss;
449 result = _plug_ipfromstring(utils, addr,
450 (struct sockaddr *)&ss, sizeof(ss));
451 if (result != SASL_OK) {
452 /* couldn't get local IP address */
455 /* Kerberos_V4 supports only IPv4 */
456 if (((struct sockaddr *)&ss)->sa_family != AF_INET)
458 memcpy(out, &ss, sizeof(struct sockaddr_in));
465 kerberosv4_server_mech_new(void *glob_context __attribute__((unused)),
466 sasl_server_params_t *sparams,
467 const char *challenge __attribute__((unused)),
468 unsigned challen __attribute__((unused)),
471 return new_text(sparams->utils, (context_t **) conn_context);
474 static int kerberosv4_server_mech_step(void *conn_context,
475 sasl_server_params_t *sparams,
476 const char *clientin,
477 unsigned clientinlen,
478 const char **serverout,
479 unsigned *serveroutlen,
480 sasl_out_params_t *oparams)
482 context_t *text = (context_t *) conn_context;
488 switch (text->state) {
491 /* random 32-bit number */
494 /* shouldn't we check for erroneous client input here?!? */
496 sparams->utils->rand(sparams->utils->rpool,(char *) &randocts ,
498 text->challenge=randocts;
499 nchal = htonl(text->challenge);
501 result = _plug_buf_alloc(text->utils, &text->out_buf,
502 &text->out_buf_len, 5);
503 if (result != SASL_OK) return result;
505 memcpy(text->out_buf,&nchal,4);
506 *serverout = text->out_buf;
511 return SASL_CONTINUE;
516 unsigned char sout[8];
520 struct sockaddr_in addr;
523 /* received authenticator */
526 if (clientinlen > MAX_KTXT_LEN) {
527 text->utils->seterror(text->utils->conn,0,
528 "request larger than maximum ticket size");
532 ticket.length=clientinlen;
533 for (lup = 0; lup < clientinlen; lup++)
534 ticket.dat[lup] = clientin[lup];
536 KRB_LOCK_MUTEX(sparams->utils);
538 text->realm = krb_realmofhost(sparams->serverFQDN);
541 strncpy (text->instance, krb_get_phost (sparams->serverFQDN),
542 sizeof (text->instance));
544 KRB_UNLOCK_MUTEX(sparams->utils);
546 text->instance[sizeof(text->instance)-1] = 0;
548 /* At some sites, krb_get_phost() sensibly but
549 * atypically returns FQDNs, versus the first component,
550 * which is what we need for RFC2222 section 7.1
552 dot = strchr(text->instance, '.');
553 if (dot) *dot = '\0';
555 memset(&addr, 0, sizeof(struct sockaddr_in));
557 #ifndef KRB4_IGNORE_IP_ADDRESS
558 /* (we ignore IP addresses in krb4 tickets at CMU to facilitate moving
559 from machine to machine) */
561 /* get ip number in addr*/
562 result = ipv4_ipfromstring(sparams->utils, sparams->ipremoteport, &addr);
563 if (result != SASL_OK || !sparams->ipremoteport) {
564 SETERROR(text->utils, "couldn't get remote IP address");
571 KRB_LOCK_MUTEX(sparams->utils);
572 result = krb_rd_req(&ticket, (char *) sparams->service, text->instance,
573 addr.sin_addr.s_addr, &ad, srvtab);
574 KRB_UNLOCK_MUTEX(sparams->utils);
576 if (result) { /* if fails mechanism fails */
577 text->utils->seterror(text->utils->conn,0,
578 "krb_rd_req failed service=%s instance=%s error code=%s (%i)",
579 sparams->service, text->instance,get_krb_err_txt(result),result);
586 * 6-8max cipher text buffer size
587 * use DES ECB in the session key
590 nchal=htonl(text->challenge+1);
591 memcpy(sout, &nchal, 4);
593 if (cando_sec(&sparams->props, sparams->external_ssf,
595 sout[4] |= KRB_SECFLAG_NONE;
596 if (cando_sec(&sparams->props, sparams->external_ssf,
597 KRB_SECFLAG_INTEGRITY))
598 sout[4] |= KRB_SECFLAG_INTEGRITY;
599 if (cando_sec(&sparams->props, sparams->external_ssf,
600 KRB_SECFLAG_ENCRYPTION))
601 sout[4] |= KRB_SECFLAG_ENCRYPTION;
602 if (cando_sec(&sparams->props, sparams->external_ssf,
603 KRB_SECFLAG_CREDENTIALS))
604 sout[4] |= KRB_SECFLAG_CREDENTIALS;
606 if(sparams->props.maxbufsize) {
607 int tmpmaxbuf = (sparams->props.maxbufsize > 0xFFFFFF) ? 0xFFFFFF : sparams->props.maxbufsize;
609 sout[5]=((tmpmaxbuf >> 16) & 0xFF);
610 sout[6]=((tmpmaxbuf >> 8) & 0xFF);
611 sout[7]=(tmpmaxbuf & 0xFF);
613 /* let's say we can support up to 64K */
614 /* no inherent inability with our layers to support more */
616 sout[5]=0x00; /* max ciphertext buffer size */
621 memcpy(text->session, ad.session, 8);
622 memcpy(text->pname, ad.pname, sizeof(text->pname));
623 memcpy(text->pinst, ad.pinst, sizeof(text->pinst));
624 memcpy(text->prealm, ad.prealm, sizeof(text->prealm));
625 des_key_sched(&ad.session, text->init_keysched);
627 /* make keyschedule for encryption and decryption */
628 des_key_sched(&ad.session, text->enc_keysched);
629 des_key_sched(&ad.session, text->dec_keysched);
631 des_ecb_encrypt((des_cblock *)sout,
636 result = _plug_buf_alloc(text->utils, &text->out_buf,
637 &text->out_buf_len, 9);
638 if(result != SASL_OK)
641 memcpy(text->out_buf,&sout,8);
642 *serverout = text->out_buf;
647 return SASL_CONTINUE;
656 if ((clientinlen == 0) || (clientinlen % 8 != 0)) {
657 text->utils->seterror(text->utils->conn,0,
658 "Response to challengs is not a multiple of 8 octets (a DES block)");
662 /* we need to make a copy because des does in place decrpytion */
663 in = sparams->utils->malloc(clientinlen + 1);
665 MEMERROR(sparams->utils);
669 memcpy(in, clientin, clientinlen);
670 in[clientinlen] = '\0';
672 /* decrypt; verify checksum */
674 des_pcbc_encrypt((des_cblock *)in,
681 testnum = (in[0]*256*256*256)+(in[1]*256*256)+(in[2]*256)+in[3];
683 if (testnum != text->challenge) {
684 SETERROR(sparams->utils, "incorrect response to challenge");
688 if (!cando_sec(&sparams->props, sparams->external_ssf,
689 in[4] & KRB_SECFLAGS)) {
690 SETERROR(sparams->utils,
691 "invalid security property specified");
695 oparams->encode = &kerberosv4_encode;
696 oparams->decode = &kerberosv4_decode;
698 switch (in[4] & KRB_SECFLAGS) {
699 case KRB_SECFLAG_NONE:
700 text->sec_type = KRB_SEC_NONE;
701 oparams->encode = NULL;
702 oparams->decode = NULL;
703 oparams->mech_ssf = 0;
705 case KRB_SECFLAG_INTEGRITY:
706 text->sec_type = KRB_SEC_INTEGRITY;
707 oparams->mech_ssf = KRB_INTEGRITY_BITS;
709 case KRB_SECFLAG_ENCRYPTION:
710 text->sec_type = KRB_SEC_ENCRYPTION;
711 oparams->mech_ssf = KRB_DES_SECURITY_BITS;
714 /* Mark that we tried */
715 oparams->mech_ssf = 2;
716 SETERROR(sparams->utils, "not a supported encryption layer");
721 /* get ip number in addr*/
722 result = ipv4_ipfromstring(sparams->utils,
723 sparams->iplocalport, &(text->ip_local));
724 if (result != SASL_OK) {
725 SETERROR(sparams->utils, "couldn't get local ip address");
726 /* couldn't get local IP address */
730 result = ipv4_ipfromstring(sparams->utils,
731 sparams->ipremoteport, &(text->ip_remote));
732 if (result != SASL_OK) {
733 SETERROR(sparams->utils, "couldn't get remote ip address");
734 /* couldn't get remote IP address */
738 /* fill in oparams */
739 oparams->maxoutbuf = (in[5] << 16) + (in[6] << 8) + in[7];
740 if(oparams->mech_ssf) {
741 /* FIXME: Likely to be too large */
742 oparams->maxoutbuf -= 50;
745 if (sparams->canon_user) {
746 char *user=NULL, *authid=NULL;
747 size_t ulen = 0, alen = strlen(text->pname);
748 int ret, cflag = SASL_CU_AUTHID;
750 if (text->pinst[0]) {
751 alen += strlen(text->pinst) + 1 /* for the . */;
754 if (strcmp(text->realm, text->prealm)) {
755 alen += strlen(text->prealm) + 1 /* for the @ */;
759 authid = sparams->utils->malloc(alen + 1);
761 MEMERROR(sparams->utils);
765 strcpy(authid, text->pname);
766 if (text->pinst[0]) {
768 strcat(authid, text->pinst);
772 strcat(authid, text->prealm);
776 user = sparams->utils->malloc(strlen((char *) in + 8) + 1);
778 MEMERROR(sparams->utils);
782 strcpy(user, (char *) in + 8);
785 cflag |= SASL_CU_AUTHZID;
788 ret = sparams->canon_user(sparams->utils->conn, authid, alen,
790 sparams->utils->free(authid);
791 if (ret != SASL_OK) {
793 sparams->utils->free(user);
798 ret = sparams->canon_user(sparams->utils->conn, user, ulen,
799 SASL_CU_AUTHZID, oparams);
801 sparams->utils->free(user);
804 if (ret != SASL_OK) return ret;
807 /* nothing more to do; authenticated */
808 oparams->doneflag = 1;
809 oparams->param_version = 0;
812 _plug_decode_init(&text->decode_context, text->utils,
813 (sparams->props.maxbufsize > 0xFFFFFF) ? 0xFFFFFF :
814 sparams->props.maxbufsize);
816 sparams->utils->free(in);
822 sparams->utils->log(NULL, SASL_LOG_ERR,
823 "Invalid Kerberos server step %d\n", text->state);
827 return SASL_FAIL; /* should never get here */
830 static int kerberosv4_mech_avail(void *glob_context __attribute__((unused)),
831 sasl_server_params_t *sparams,
832 void **conn_context __attribute__((unused)))
834 struct sockaddr_in addr;
836 if (!sparams->iplocalport || !sparams->ipremoteport
837 || ipv4_ipfromstring(sparams->utils,
838 sparams->iplocalport, &addr) != SASL_OK
839 || ipv4_ipfromstring(sparams->utils,
840 sparams->ipremoteport, &addr) != SASL_OK) {
841 SETERROR(sparams->utils,
842 "KERBEROS_V4 unavailable due to lack of IPv4 information");
850 static sasl_server_plug_t kerberosv4_server_plugins[] =
853 "KERBEROS_V4", /* mech_name */
854 KRB_DES_SECURITY_BITS, /* max_ssf */
857 | SASL_SEC_NOANONYMOUS
858 | SASL_SEC_MUTUAL_AUTH, /* security_flags */
859 SASL_FEAT_SERVER_FIRST
860 | SASL_FEAT_ALLOWS_PROXY, /* features */
861 NULL, /* glob_context */
862 &kerberosv4_server_mech_new, /* mech_new */
863 &kerberosv4_server_mech_step, /* mech_step */
864 &kerberosv4_common_mech_dispose,/* mech_dispose */
865 &kerberosv4_common_mech_free, /* mech_free */
867 NULL, /* user_query */
869 &kerberosv4_mech_avail, /* mech_avail */
873 #endif /* macintosh */
875 int kerberos4_server_plug_init(const sasl_utils_t *utils,
878 sasl_server_plug_t **pluglist,
887 if (maxversion < SASL_SERVER_PLUG_VERSION) {
893 krb_mutex = utils->mutex_alloc();
900 utils->getopt(utils->getopt_context,
901 "KERBEROS_V4", "srvtab", &ret, &rl);
908 srvtab = utils->malloc(sizeof(char) * (rl + 1));
919 /* fail if we can't open the srvtab file */
920 if (access(srvtab, R_OK) != 0) {
921 utils->log(NULL, SASL_LOG_ERR,
922 "can't access srvtab file %s: %m", srvtab, errno);
930 *out_version = SASL_SERVER_PLUG_VERSION;
931 *pluglist = kerberosv4_server_plugins;
938 /***************************** Client Section *****************************/
941 kerberosv4_client_mech_new(void *glob_context __attribute__((unused)),
942 sasl_client_params_t *params,
945 return new_text(params->utils, (context_t **) conn_context);
948 static int kerberosv4_client_mech_step(void *conn_context,
949 sasl_client_params_t *cparams,
950 const char *serverin,
951 unsigned serverinlen,
952 sasl_interact_t **prompt_need,
953 const char **clientout,
954 unsigned *clientoutlen,
955 sasl_out_params_t *oparams)
957 context_t *text = (context_t *) conn_context;
964 authent.length = MAX_KTXT_LEN;
966 switch (text->state) {
969 /* We should've just recieved a 32-bit number in network byte order.
970 * We want to reply with an authenticator. */
975 memset(&ticket, 0L, sizeof(ticket));
976 ticket.length = MAX_KTXT_LEN;
978 if (serverinlen != 4) {
979 text->utils->seterror(text->utils->conn, 0,
980 "server challenge not 4 bytes long");
984 memcpy(&text->challenge, serverin, 4);
986 text->challenge=ntohl(text->challenge);
988 if (cparams->serverFQDN == NULL) {
989 cparams->utils->log(NULL, SASL_LOG_ERR,
990 "no 'serverFQDN' set");
991 SETERROR(text->utils, "paramater error");
992 return SASL_BADPARAM;
994 if (cparams->service == NULL) {
995 cparams->utils->log(NULL, SASL_LOG_ERR,
997 SETERROR(text->utils, "paramater error");
998 return SASL_BADPARAM;
1001 KRB_LOCK_MUTEX(cparams->utils);
1002 text->realm=krb_realmofhost(cparams->serverFQDN);
1003 text->hostname=(char *) cparams->serverFQDN;
1005 /* the instance of the principal we're authenticating with */
1006 strncpy (text->instance, krb_get_phost (cparams->serverFQDN),
1007 sizeof (text->instance));
1009 /* text->instance is NULL terminated unless it was too long */
1010 text->instance[sizeof(text->instance)-1] = '\0';
1012 /* At some sites, krb_get_phost() sensibly but
1013 * atypically returns FQDNs, versus the first component,
1014 * which is what we need for RFC2222 section 7.1
1016 dot = strchr(text->instance, '.');
1017 if (dot) *dot = '\0';
1020 if ((result = krb_mk_req(&ticket, (char *) cparams->service,
1021 text->instance, text->realm, text->challenge)))
1023 memset(&text->credentials,0,sizeof(text->credentials));
1024 if (kcglue_krb_mk_req(ticket.dat,
1030 &text->credentials.session,
1031 text->credentials.pname,
1032 text->credentials.pinst) != 0)
1035 KRB_UNLOCK_MUTEX(cparams->utils);
1037 text->utils->seterror(text->utils->conn,SASL_NOLOG,
1038 "krb_mk_req() failed");
1040 cparams->utils->log(NULL, SASL_LOG_ERR,
1041 "krb_mk_req() failed: %s (%d)",
1042 get_krb_err_txt(result), result);
1046 KRB_UNLOCK_MUTEX(cparams->utils);
1048 ret = _plug_buf_alloc(text->utils, &(text->out_buf),
1049 &(text->out_buf_len), ticket.length);
1050 if (ret != SASL_OK) return ret;
1052 memcpy(text->out_buf, ticket.dat, ticket.length);
1054 *clientout = text->out_buf;
1055 *clientoutlen = ticket.length;
1059 return SASL_CONTINUE;
1068 unsigned char *sout = NULL;
1070 unsigned char in[8];
1074 int user_result = SASL_OK;
1076 /* try to get the authid */
1077 if (text->user == NULL) {
1078 user_result = _plug_get_userid(cparams->utils, &text->user,
1081 if (user_result != SASL_OK && user_result != SASL_INTERACT)
1085 /* free prompts we got */
1086 if (prompt_need && *prompt_need) {
1087 cparams->utils->free(*prompt_need);
1088 *prompt_need = NULL;
1091 /* if there are prompts not filled in */
1092 if (user_result == SASL_INTERACT) {
1093 /* make the prompt list */
1095 _plug_make_prompts(cparams->utils, prompt_need,
1096 user_result == SASL_INTERACT ?
1097 "Please enter your authorization name" : NULL, NULL,
1102 if (result!=SASL_OK) return result;
1104 return SASL_INTERACT;
1107 /* must be 8 octets */
1108 if (serverinlen!=8) {
1109 SETERROR(cparams->utils,
1110 "server response not 8 bytes long");
1111 return SASL_BADAUTH;
1114 memcpy(in, serverin, 8);
1117 /* get credentials */
1118 KRB_LOCK_MUTEX(cparams->utils);
1119 result = krb_get_cred((char *)cparams->service,
1122 &text->credentials);
1123 KRB_UNLOCK_MUTEX(cparams->utils);
1126 cparams->utils->log(NULL, SASL_LOG_ERR,
1127 "krb_get_cred() failed: %s (%d)",
1128 get_krb_err_txt(result), result);
1129 SETERROR(cparams->utils, "krb_get_cred() failed");
1130 return SASL_BADAUTH;
1133 memcpy(text->session, text->credentials.session, 8);
1135 /* make key schedule for encryption and decryption */
1136 des_key_sched(&text->session, text->init_keysched);
1137 des_key_sched(&text->session, text->enc_keysched);
1138 des_key_sched(&text->session, text->dec_keysched);
1140 /* decrypt from server */
1141 des_ecb_encrypt((des_cblock *)in, (des_cblock *)in,
1142 text->init_keysched, DES_DECRYPT);
1144 /* convert to 32bit int */
1145 testnum = (in[0]*256*256*256)+(in[1]*256*256)+(in[2]*256)+in[3];
1147 /* verify data 1st 4 octets must be equal to chal+1 */
1148 if (testnum != text->challenge+1) {
1149 SETERROR(cparams->utils,"server response incorrect");
1150 return SASL_BADAUTH;
1153 /* construct 8 octets
1154 * 1-4 - original checksum
1155 * 5 - bitmask of sec layer
1156 * 6-8 max buffer size
1158 if (cparams->props.min_ssf >
1159 KRB_DES_SECURITY_BITS + cparams->external_ssf) {
1160 SETERROR(cparams->utils,
1161 "minimum ssf too strong for this mechanism");
1162 return SASL_TOOWEAK;
1163 } else if (cparams->props.min_ssf > cparams->props.max_ssf) {
1164 SETERROR(cparams->utils,
1165 "minimum ssf larger than maximum ssf");
1166 return SASL_BADPARAM;
1169 /* create stuff to send to server */
1171 cparams->utils->malloc(9+(text->user ? strlen(text->user) : 0)+9);
1173 MEMERROR(cparams->utils);
1177 nchal = htonl(text->challenge);
1178 memcpy(sout, &nchal, 4);
1180 /* need bits of layer */
1181 if(cparams->props.maxbufsize == 0) {
1182 need = musthave = 0;
1184 need = cparams->props.max_ssf - cparams->external_ssf;
1185 musthave = cparams->props.min_ssf - cparams->external_ssf;
1188 oparams->decode = &kerberosv4_decode;
1189 oparams->encode = &kerberosv4_encode;
1191 if ((in[4] & KRB_SECFLAG_ENCRYPTION)
1192 && (need>=56) && (musthave <= 56)) {
1194 text->sec_type = KRB_SEC_ENCRYPTION;
1195 oparams->mech_ssf = 56;
1196 sout[4] = KRB_SECFLAG_ENCRYPTION;
1197 /* using encryption layer */
1198 } else if ((in[4] & KRB_SECFLAG_INTEGRITY)
1199 && (need >= 1) && (musthave <= 1)) {
1201 text->sec_type = KRB_SEC_INTEGRITY;
1202 oparams->mech_ssf=1;
1203 sout[4] = KRB_SECFLAG_INTEGRITY;
1204 /* using integrity layer */
1205 } else if ((in[4] & KRB_SECFLAG_NONE) && (musthave <= 0)) {
1207 text->sec_type = KRB_SEC_NONE;
1208 oparams->encode=NULL;
1209 oparams->decode=NULL;
1210 oparams->mech_ssf=0;
1211 sout[4] = KRB_SECFLAG_NONE;
1213 /* Mark that we tried */
1214 oparams->mech_ssf=2;
1215 SETERROR(cparams->utils,
1216 "unable to agree on layers with server");
1217 return SASL_BADPROT;
1220 servermaxbuf = in[5]*256*256+in[6]*256+in[7];
1221 oparams->maxoutbuf = servermaxbuf;
1222 if (oparams->mech_ssf) {
1223 /* FIXME: Likely to be too large */
1224 oparams->maxoutbuf -= 50;
1227 if(cparams->props.maxbufsize) {
1228 int tmpmaxbuf = ( cparams->props.maxbufsize > 0xFFFFFF ) ? 0xFFFFFF : cparams->props.maxbufsize;
1230 sout[5]=((tmpmaxbuf >> 16) & 0xFF);
1231 sout[6]=((tmpmaxbuf >> 8) & 0xFF);
1232 sout[7]=(tmpmaxbuf & 0xFF);
1234 /* let's say we can support up to 64K */
1235 /* no inherent inability with our layers to support more */
1237 sout[5]=0x00; /* max ciphertext buffer size */
1242 sout[8] = 0x00; /* just to be safe */
1245 len = 9; /* 8 + trailing NULL */
1247 strcpy((char *)sout + 8, text->user);
1248 len += strlen(text->user);
1251 /* append 0 based octets so is multiple of 8 */
1258 des_pcbc_encrypt((des_cblock *)sout,
1261 text->init_keysched,
1262 (des_cblock *)text->session,
1265 result = _plug_buf_alloc(text->utils, &text->out_buf,
1266 &text->out_buf_len, len);
1267 if (result != SASL_OK) return result;
1269 memcpy(text->out_buf, sout, len);
1271 *clientout = text->out_buf;
1272 *clientoutlen = len;
1274 /* nothing more to do; should be authenticated */
1275 if(cparams->iplocalport) {
1276 result = ipv4_ipfromstring(cparams->utils,
1277 cparams->iplocalport,
1279 if (result != SASL_OK) {
1280 /* couldn't get local IP address */
1285 if (cparams->ipremoteport) {
1286 result = ipv4_ipfromstring(cparams->utils,
1287 cparams->ipremoteport,
1288 &(text->ip_remote));
1289 if (result != SASL_OK) {
1290 /* couldn't get local IP address */
1295 buf = cparams->utils->malloc(strlen(text->credentials.pname)
1296 + strlen(text->credentials.pinst)
1299 MEMERROR(cparams->utils);
1302 strcpy(buf, text->credentials.pname);
1303 if (text->credentials.pinst[0]) {
1305 strcat(buf, text->credentials.pinst);
1308 if (text->user && !text->user[0]) {
1312 ret = cparams->canon_user(cparams->utils->conn, buf, 0,
1313 SASL_CU_AUTHID, oparams);
1314 if (ret != SASL_OK) {
1315 cparams->utils->free(buf);
1316 cparams->utils->free(sout);
1321 /* 0 in length fields means use strlen() */
1322 ret = cparams->canon_user(cparams->utils->conn, buf, 0,
1323 SASL_CU_AUTHZID, oparams);
1325 ret = cparams->canon_user(cparams->utils->conn, text->user, 0,
1326 SASL_CU_AUTHZID, oparams);
1329 cparams->utils->free(buf);
1331 oparams->doneflag = 1;
1332 oparams->param_version = 0;
1334 /* used by layers */
1335 _plug_decode_init(&text->decode_context, text->utils,
1336 (cparams->props.maxbufsize > 0xFFFFFF) ? 0xFFFFFF :
1337 cparams->props.maxbufsize);
1339 if (sout) cparams->utils->free(sout);
1345 cparams->utils->log(NULL, SASL_LOG_ERR,
1346 "Invalid Kerberos client step %d\n", text->state);
1350 return SASL_FAIL; /* should never get here */
1353 static const long kerberosv4_required_prompts[] = {
1357 static sasl_client_plug_t kerberosv4_client_plugins[] =
1360 "KERBEROS_V4", /* mech_name */
1361 KRB_DES_SECURITY_BITS, /* max_ssf */
1362 SASL_SEC_NOPLAINTEXT
1364 | SASL_SEC_NOANONYMOUS
1365 | SASL_SEC_MUTUAL_AUTH, /* security_flags */
1366 SASL_FEAT_NEEDSERVERFQDN
1367 | SASL_FEAT_SERVER_FIRST
1368 | SASL_FEAT_ALLOWS_PROXY, /* features */
1369 kerberosv4_required_prompts, /* required_prompts */
1370 NULL, /* glob_context */
1371 &kerberosv4_client_mech_new, /* mech_new */
1372 &kerberosv4_client_mech_step, /* mech_step */
1373 &kerberosv4_common_mech_dispose,/* mech_dispose */
1374 &kerberosv4_common_mech_free, /* mech_free */
1381 int kerberos4_client_plug_init(const sasl_utils_t *utils,
1384 sasl_client_plug_t **pluglist,
1387 if (maxversion < SASL_CLIENT_PLUG_VERSION) {
1388 SETERROR(utils, "Wrong KERBEROS_V4 version");
1389 return SASL_BADVERS;
1393 krb_mutex = utils->mutex_alloc();
1399 *out_version = SASL_CLIENT_PLUG_VERSION;
1400 *pluglist = kerberosv4_client_plugins;