1/* Kerberos4 SASL plugin
2 * Rob Siemborski
3 * Tim Martin
4 * $Id: kerberos4.c,v 1.100 2009/03/10 16:27:52 mel Exp $
5 */
6/*
7 * Copyright (c) 1998-2003 Carnegie Mellon University.  All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 *
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 *
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
19 *    distribution.
20 *
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
27 *      5000 Forbes Avenue
28 *      Pittsburgh, PA  15213-3890
29 *      (412) 268-4387, fax: (412) 268-7395
30 *      tech-transfer@andrew.cmu.edu
31 *
32 * 4. Redistributions of any form whatsoever must retain the following
33 *    acknowledgment:
34 *    "This product includes software developed by Computing Services
35 *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
36 *
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.
44 */
45
46#include <config.h>
47#include <stdlib.h>
48#include <string.h>
49#include <krb.h>
50
51#ifdef WITH_DES
52# ifdef WITH_SSL_DES
53#  include <openssl/des.h>
54# else
55#  include <des.h>
56# endif /* WITH_SSL_DES */
57#endif /* WITH_DES */
58
59#ifdef WIN32
60# include <winsock2.h>
61#elif defined(macintosh)
62#include <kcglue_krb.h>
63#else
64# include <sys/param.h>
65# include <sys/socket.h>
66# include <netinet/in.h>
67# include <arpa/inet.h>
68# include <netdb.h>
69#endif /* WIN32 */
70#ifdef HAVE_UNISTD_H
71#include <unistd.h>
72#endif
73#include <fcntl.h>
74#include <sasl.h>
75#include <saslutil.h>
76#include <saslplug.h>
77
78#include <errno.h>
79#include <ctype.h>
80
81#include "plugin_common.h"
82
83#ifdef macintosh
84/*
85 * krb.h doenst include some functions and mac compiler is picky
86 * about declartions
87 */
88#include <extra_krb.h>
89#include <sasl_kerberos4_plugin_decl.h>
90#endif
91
92#ifdef WIN32
93/* This must be after sasl.h, saslutil.h */
94# include "saslKERBEROSV4.h"
95
96/* KClient doesn't define this */
97typedef struct krb_principal {
98    char name[ANAME_SZ];
99    char instance[INST_SZ];
100    char realm[REALM_SZ];
101} krb_principal;
102
103/* This isn't defined under WIN32.  For access() */
104#ifndef R_OK
105#define R_OK 04
106#endif
107/* we also need io.h for access() prototype */
108#include <io.h>
109#endif /* WIN32 */
110
111#ifdef sun
112/* gotta define gethostname ourselves on suns */
113extern int gethostname(char *, int);
114#endif
115
116/*****************************  Common Section  *****************************/
117
118static const char plugin_id[] = "$Id: kerberos4.c,v 1.100 2009/03/10 16:27:52 mel Exp $";
119
120#ifndef KEYFILE
121#define KEYFILE "/etc/srvtab";
122#endif
123
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)
129
130#define KRB_DES_SECURITY_BITS (56)
131#define KRB_INTEGRITY_BITS (1)
132
133typedef enum Krb_sec {
134    KRB_SEC_NONE = 0,
135    KRB_SEC_INTEGRITY = 1,
136    KRB_SEC_ENCRYPTION = 2
137} Krb_sec_t;
138
139typedef struct context {
140    int state;
141
142    int challenge;         /* this is the challenge (32 bit int) used
143			      for the authentication */
144
145    char *service;                   /* kerberos service */
146    char instance[ANAME_SZ];
147    char pname[ANAME_SZ];
148    char pinst[INST_SZ];
149    char prealm[REALM_SZ];
150    char *hostname;                  /* hostname */
151    char *realm;                     /* kerberos realm */
152    char *auth;                      /* */
153
154    CREDENTIALS credentials;
155
156    des_cblock key;                  /* session key */
157    des_cblock session;              /* session key */
158
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 */
162
163
164    struct sockaddr_in ip_local;     /* local ip address and port.
165					needed for layers */
166    struct sockaddr_in ip_remote;    /* remote ip address and port.
167					needed for layers */
168
169    const sasl_utils_t *utils;       /* this is useful to have around */
170
171    Krb_sec_t sec_type;
172    char *encode_buf;                /* For encoding/decoding mem management */
173    char *decode_buf;
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;
179
180    decode_context_t decode_context;
181
182    char *out_buf;                   /* per-step mem management */
183    unsigned out_buf_len;
184
185    const char *user;                      /* used by client */
186
187    int secflags; /* client/server supports layers? */
188
189    long time_sec; /* These are used to make sure we are getting */
190    char time_5ms; /* strictly increasing timestamps */
191
192} context_t;
193
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"); \
198			           return SASL_FAIL; \
199                                }
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"); \
204			           return SASL_FAIL; \
205                                }
206
207/* Mutex for not-thread-safe kerberos 4 library */
208static void *krb_mutex = NULL;
209static char *srvtab = NULL;
210static unsigned refcount = 0;
211
212static int kerberosv4_encode(void *context,
213			     const struct iovec *invec,
214			     unsigned numiov,
215			     const char **output,
216			     unsigned *outputlen)
217{
218    int len, ret;
219    context_t *text = (context_t *)context;
220    struct buffer_info *inblob, bufinfo;
221
222    if(numiov > 1) {
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;
226    } else {
227	bufinfo.data = invec[0].iov_base;
228	bufinfo.curlen = invec[0].iov_len;
229	inblob = &bufinfo;
230    }
231
232    ret = _plug_buf_alloc(text->utils, &(text->encode_buf),
233			  &text->encode_buf_len, inblob->curlen+40);
234
235    if(ret != SASL_OK) return ret;
236
237    KRB_LOCK_MUTEX(text->utils);
238
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
243	   MIT kerberos. */
244	len=krb_mk_priv(inblob->data, (text->encode_buf+4),
245			inblob->curlen,  text->init_keysched,
246			&text->session, &text->ip_local,
247			&text->ip_remote);
248    } else if (text->sec_type == KRB_SEC_INTEGRITY) {
249	len=krb_mk_safe(inblob->data, (text->encode_buf+4),
250			inblob->curlen,
251			&text->session, &text->ip_local, &text->ip_remote);
252    } else {
253	len = -1;
254    }
255
256    KRB_UNLOCK_MUTEX(text->utils);
257
258    /* returns -1 on error */
259    if (len==-1) return SASL_FAIL;
260
261    /* now copy in the len of the buffer in network byte order */
262    *outputlen=len+4;
263    len=htonl(len);
264    memcpy(text->encode_buf, &len, 4);
265
266    /* Setup the const pointer */
267    *output = text->encode_buf;
268
269    return SASL_OK;
270}
271
272static int kerberosv4_decode_packet(void *context,
273				    const char *input, unsigned inputlen,
274				    char **output, unsigned *outputlen)
275{
276    context_t *text = (context_t *) context;
277    int result;
278    MSG_DAT data;
279
280    memset(&data,0,sizeof(MSG_DAT));
281
282    KRB_LOCK_MUTEX(text->utils);
283
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);
292    } else {
293        KRB_UNLOCK_MUTEX(text->utils);
294	text->utils->seterror(text->utils->conn, 0,
295			      "KERBEROS_4 decode called with KRB_SEC_NONE");
296	return SASL_FAIL;
297    }
298
299    KRB_UNLOCK_MUTEX(text->utils);
300
301    /* see if the krb library gave us a failure */
302    if (result != 0) {
303	text->utils->seterror(text->utils->conn, 0, get_krb_err_txt(result));
304	return SASL_FAIL;
305    }
306
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))))
311	{
312	    text->utils->seterror(text->utils->conn, 0, "timestamps not ok");
313	    return SASL_FAIL;
314	}
315
316    text->time_sec = data.time_sec;
317    text->time_5ms = data.time_5ms;
318
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)
323	return result;
324
325    *output = text->decode_once_buf;
326    *outputlen = data.app_length;
327    memcpy(*output, data.app_data, data.app_length);
328    (*output)[*outputlen] = '\0';
329
330    return SASL_OK;
331}
332
333static int kerberosv4_decode(void *context,
334			     const char *input, unsigned inputlen,
335			     const char **output, unsigned *outputlen)
336{
337    context_t *text = (context_t *) context;
338    int ret;
339
340    ret = _plug_decode(&text->decode_context, input, inputlen,
341		       &text->decode_buf, &text->decode_buf_len, outputlen,
342		       kerberosv4_decode_packet, text);
343
344    *output = text->decode_buf;
345
346    return ret;
347}
348
349static int new_text(const sasl_utils_t *utils, context_t **text)
350{
351    context_t *ret = (context_t *) utils->malloc(sizeof(context_t));
352
353    if (ret == NULL) {
354	MEMERROR(utils);
355	return SASL_NOMEM;
356    }
357
358    memset(ret, 0, sizeof(context_t));
359
360    ret->state = 1;
361    ret->utils = utils;
362
363    *text = ret;
364
365    return SASL_OK;
366}
367
368static void kerberosv4_common_mech_dispose(void *conn_context,
369					   const sasl_utils_t *utils)
370{
371    context_t *text = (context_t *)conn_context;
372
373    if(!text) return;
374
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);
383    }
384    /* no need to free userid, it's just the interaction result */
385
386    utils->free(text);
387}
388
389static void
390kerberosv4_common_mech_free(void *glob_context __attribute__((unused)),
391			    const sasl_utils_t *utils)
392{
393    if (krb_mutex) {
394	utils->mutex_free(krb_mutex);
395	krb_mutex = NULL; /* in case we need to re-use it */
396    }
397    refcount--;
398    if (srvtab && !refcount) {
399	utils->free(srvtab);
400	srvtab = NULL;
401    }
402}
403
404/*****************************  Server Section  *****************************/
405
406static int cando_sec(sasl_security_properties_t *props,
407		     int external_ssf,
408		     int secflag)
409{
410    int need;
411    int musthave;
412
413    if(props->maxbufsize == 0) {
414	need = musthave = 0;
415    } else {
416	need = props->max_ssf - external_ssf;
417	musthave = props->min_ssf - external_ssf;
418    }
419
420    switch (secflag) {
421    case KRB_SECFLAG_NONE:
422	if (musthave <= 0)
423	    return 1;
424	break;
425    case KRB_SECFLAG_INTEGRITY:
426	if ((musthave <= KRB_INTEGRITY_BITS)
427	    && (KRB_INTEGRITY_BITS <= need))
428	    return 1;
429	break;
430    case KRB_SECFLAG_ENCRYPTION:
431	if ((musthave <= KRB_DES_SECURITY_BITS)
432	    && (KRB_DES_SECURITY_BITS <= need))
433	    return 1;
434	break;
435    case KRB_SECFLAG_CREDENTIALS:
436	if (props->security_flags & SASL_SEC_PASS_CREDENTIALS)
437	    return 1;
438	break;
439    }
440    return 0;
441}
442
443static int ipv4_ipfromstring(const sasl_utils_t *utils, const char *addr,
444			     struct sockaddr_in *out)
445{
446    struct sockaddr_storage ss;
447    int result;
448
449    result = _plug_ipfromstring(utils, addr,
450				(struct sockaddr *)&ss, sizeof(ss));
451    if (result != SASL_OK) {
452	/* couldn't get local IP address */
453	return result;
454    }
455    /* Kerberos_V4 supports only IPv4 */
456    if (((struct sockaddr *)&ss)->sa_family != AF_INET)
457	return SASL_FAIL;
458    memcpy(out, &ss, sizeof(struct sockaddr_in));
459
460    return SASL_OK;
461}
462
463#ifndef macintosh
464static int
465kerberosv4_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)),
469			   void **conn_context)
470{
471    return new_text(sparams->utils, (context_t **) conn_context);
472}
473
474static 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)
481{
482    context_t *text = (context_t *) conn_context;
483    int result;
484
485    *serverout = NULL;
486    *serveroutlen = 0;
487
488    switch (text->state) {
489
490    case 1: {
491	/* random 32-bit number */
492	int randocts, nchal;
493
494	/* shouldn't we check for erroneous client input here?!? */
495
496	sparams->utils->rand(sparams->utils->rpool,(char *) &randocts ,
497			     sizeof(randocts));
498	text->challenge=randocts;
499	nchal = htonl(text->challenge);
500
501	result = _plug_buf_alloc(text->utils, &text->out_buf,
502				 &text->out_buf_len, 5);
503	if (result != SASL_OK) return result;
504
505	memcpy(text->out_buf,&nchal,4);
506	*serverout = text->out_buf;
507	*serveroutlen = 4;
508
509	text->state = 2;
510
511	return SASL_CONTINUE;
512    }
513
514    case 2: {
515	int nchal;
516	unsigned char sout[8];
517	AUTH_DAT ad;
518	KTEXT_ST ticket;
519	unsigned lup;
520	struct sockaddr_in addr;
521	char *dot;
522
523	/* received authenticator */
524
525	/* create ticket */
526	if (clientinlen > MAX_KTXT_LEN) {
527	    text->utils->seterror(text->utils->conn,0,
528				  "request larger than maximum ticket size");
529	    return SASL_FAIL;
530	}
531
532	ticket.length=clientinlen;
533	for (lup = 0; lup < clientinlen; lup++)
534	    ticket.dat[lup] = clientin[lup];
535
536	KRB_LOCK_MUTEX(sparams->utils);
537
538	text->realm = krb_realmofhost(sparams->serverFQDN);
539
540	/* get instance */
541	strncpy (text->instance, krb_get_phost (sparams->serverFQDN),
542		 sizeof (text->instance));
543
544	KRB_UNLOCK_MUTEX(sparams->utils);
545
546	text->instance[sizeof(text->instance)-1] = 0;
547
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
551	 */
552	dot = strchr(text->instance, '.');
553	if (dot) *dot = '\0';
554
555	memset(&addr, 0, sizeof(struct sockaddr_in));
556
557#ifndef KRB4_IGNORE_IP_ADDRESS
558	/* (we ignore IP addresses in krb4 tickets at CMU to facilitate moving
559	   from machine to machine) */
560
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");
565	    return result;
566	}
567#endif
568
569	/* check ticket */
570
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);
575
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);
580	    return SASL_BADAUTH;
581	}
582
583	/* 8 octets of data
584	 * 1-4 checksum+1
585	 * 5 security layers
586	 * 6-8max cipher text buffer size
587	 * use DES ECB in the session key
588	 */
589
590	nchal=htonl(text->challenge+1);
591	memcpy(sout, &nchal, 4);
592	sout[4]= 0;
593	if (cando_sec(&sparams->props, sparams->external_ssf,
594		      KRB_SECFLAG_NONE))
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;
605
606	if(sparams->props.maxbufsize) {
607	    int tmpmaxbuf = (sparams->props.maxbufsize > 0xFFFFFF) ? 0xFFFFFF : sparams->props.maxbufsize;
608
609	    sout[5]=((tmpmaxbuf >> 16) & 0xFF);
610	    sout[6]=((tmpmaxbuf >> 8) & 0xFF);
611	    sout[7]=(tmpmaxbuf & 0xFF);
612	} else {
613            /* let's say we can support up to 64K */
614	    /* no inherent inability with our layers to support more */
615
616	    sout[5]=0x00;  /* max ciphertext buffer size */
617	    sout[6]=0xFF;
618	    sout[7]=0xFF;
619	}
620
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);
626
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);
630
631	des_ecb_encrypt((des_cblock *)sout,
632			(des_cblock *)sout,
633			text->init_keysched,
634			DES_ENCRYPT);
635
636	result = _plug_buf_alloc(text->utils, &text->out_buf,
637				 &text->out_buf_len, 9);
638	if(result != SASL_OK)
639	    return result;
640
641	memcpy(text->out_buf,&sout,8);
642	*serverout = text->out_buf;
643	*serveroutlen = 8;
644
645	text->state = 3;
646
647	return SASL_CONTINUE;
648    }
649
650    case 3: {
651	int result;
652	int testnum;
653	int flag;
654	unsigned char *in;
655
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)");
659	    return SASL_FAIL;
660	}
661
662	/* we need to make a copy because des does in place decrpytion */
663	in = sparams->utils->malloc(clientinlen + 1);
664	if (in == NULL) {
665	    MEMERROR(sparams->utils);
666	    return SASL_NOMEM;
667	}
668
669	memcpy(in, clientin, clientinlen);
670	in[clientinlen] = '\0';
671
672	/* decrypt; verify checksum */
673
674	des_pcbc_encrypt((des_cblock *)in,
675			 (des_cblock *)in,
676			 clientinlen,
677			 text->init_keysched,
678			 &text->session,
679			 DES_DECRYPT);
680
681	testnum = (in[0]*256*256*256)+(in[1]*256*256)+(in[2]*256)+in[3];
682
683	if (testnum != text->challenge) {
684	    SETERROR(sparams->utils, "incorrect response to challenge");
685	    return SASL_BADAUTH;
686	}
687
688	if (!cando_sec(&sparams->props, sparams->external_ssf,
689		       in[4] & KRB_SECFLAGS)) {
690	    SETERROR(sparams->utils,
691		     "invalid security property specified");
692	    return SASL_BADPROT;
693	}
694
695	oparams->encode = &kerberosv4_encode;
696	oparams->decode = &kerberosv4_decode;
697
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;
704	    break;
705	case KRB_SECFLAG_INTEGRITY:
706	    text->sec_type = KRB_SEC_INTEGRITY;
707	    oparams->mech_ssf = KRB_INTEGRITY_BITS;
708	    break;
709	case KRB_SECFLAG_ENCRYPTION:
710	    text->sec_type = KRB_SEC_ENCRYPTION;
711	    oparams->mech_ssf = KRB_DES_SECURITY_BITS;
712	    break;
713	default:
714	    /* Mark that we tried */
715	    oparams->mech_ssf = 2;
716	    SETERROR(sparams->utils, "not a supported encryption layer");
717	    return SASL_BADPROT;
718	}
719
720	/* get ip data */
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 */
727	    return result;
728	}
729
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 */
735	    return result;
736	}
737
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;
743	}
744
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 | SASL_CU_EXTERNALLY_VERIFIED;
749
750	    if (text->pinst[0]) {
751		alen += strlen(text->pinst) + 1 /* for the . */;
752	    }
753	    flag = 0;
754	    if (strcmp(text->realm, text->prealm)) {
755		alen += strlen(text->prealm) + 1 /* for the @ */;
756		flag = 1;
757	    }
758
759	    authid = sparams->utils->malloc(alen + 1);
760	    if (!authid) {
761		MEMERROR(sparams->utils);
762		return SASL_NOMEM;
763	    }
764
765	    strcpy(authid, text->pname);
766	    if (text->pinst[0]) {
767		strcat(authid, ".");
768		strcat(authid, text->pinst);
769	    }
770	    if (flag) {
771		strcat(authid, "@");
772		strcat(authid, text->prealm);
773	    }
774
775	    if (in[8]) {
776		user = sparams->utils->malloc(strlen((char *) in + 8) + 1);
777		if (!user) {
778		    MEMERROR(sparams->utils);
779		    return SASL_NOMEM;
780		}
781
782		strcpy(user, (char *) in + 8);
783		ulen = strlen(user);
784	    } else {
785	    	cflag |= SASL_CU_AUTHZID;
786	    }
787
788	    ret = sparams->canon_user(sparams->utils->conn, authid, alen,
789				      cflag, oparams);
790	    sparams->utils->free(authid);
791	    if (ret != SASL_OK) {
792		if (user)
793		    sparams->utils->free(user);
794		return ret;
795	    }
796
797	    if (user) {
798	    	ret = sparams->canon_user(sparams->utils->conn, user, ulen,
799				      SASL_CU_AUTHZID, oparams);
800
801		sparams->utils->free(user);
802	    }
803
804	    if (ret != SASL_OK) return ret;
805	}
806
807	/* nothing more to do; authenticated */
808	oparams->doneflag = 1;
809	oparams->param_version = 0;
810
811	/* used by layers */
812	_plug_decode_init(&text->decode_context, text->utils,
813			  (sparams->props.maxbufsize > 0xFFFFFF) ? 0xFFFFFF :
814			  sparams->props.maxbufsize);
815
816	sparams->utils->free(in);
817
818	return SASL_OK;
819    }
820
821    default:
822	sparams->utils->log(NULL, SASL_LOG_ERR,
823			    "Invalid Kerberos server step %d\n", text->state);
824	return SASL_FAIL;
825    }
826
827    return SASL_FAIL; /* should never get here */
828}
829
830static int kerberosv4_mech_avail(void *glob_context __attribute__((unused)),
831				 sasl_server_params_t *sparams,
832				 void **conn_context __attribute__((unused)))
833{
834    struct sockaddr_in addr;
835
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");
843	return SASL_NOMECH;
844    }
845
846    return SASL_OK;
847}
848
849
850static sasl_server_plug_t kerberosv4_server_plugins[] =
851{
852    {
853	"KERBEROS_V4",			/* mech_name */
854	KRB_DES_SECURITY_BITS,		/* max_ssf */
855	SASL_SEC_NOPLAINTEXT
856	| SASL_SEC_NOACTIVE
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 */
866	NULL,				/* setpass */
867	NULL,				/* user_query */
868	NULL,				/* idle */
869	&kerberosv4_mech_avail,		/* mech_avail */
870	NULL				/* spare */
871    }
872};
873#endif /* macintosh */
874
875int kerberos4_server_plug_init(const sasl_utils_t *utils,
876			       int maxversion,
877			       int *out_version,
878			       sasl_server_plug_t **pluglist,
879			       int *plugcount)
880{
881#ifdef macintosh
882    return SASL_BADVERS;
883#else
884    const char *ret;
885    unsigned int rl;
886
887    if (maxversion < SASL_SERVER_PLUG_VERSION) {
888	return SASL_BADVERS;
889    }
890
891
892    if (!krb_mutex) {
893	krb_mutex = utils->mutex_alloc();
894	if(!krb_mutex) {
895	    return SASL_FAIL;
896	}
897    }
898
899    if (!srvtab) {
900	utils->getopt(utils->getopt_context,
901		      "KERBEROS_V4", "srvtab", &ret, &rl);
902
903	if (ret == NULL) {
904	    ret = KEYFILE;
905	    rl = strlen(ret);
906	}
907
908	srvtab = utils->malloc(sizeof(char) * (rl + 1));
909	if(!srvtab) {
910	    MEMERROR(utils);
911	    return SASL_NOMEM;
912	}
913
914	strcpy(srvtab, ret);
915    }
916
917    refcount++;
918
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);
923	if(!(--refcount)) {
924	    utils->free(srvtab);
925	    srvtab=NULL;
926	}
927	return SASL_FAIL;
928    }
929
930    *out_version = SASL_SERVER_PLUG_VERSION;
931    *pluglist = kerberosv4_server_plugins;
932    *plugcount = 1;
933
934    return SASL_OK;
935#endif
936}
937
938/*****************************  Client Section  *****************************/
939
940static int
941kerberosv4_client_mech_new(void *glob_context __attribute__((unused)),
942			   sasl_client_params_t *params,
943			   void **conn_context)
944{
945    return new_text(params->utils, (context_t **) conn_context);
946}
947
948static 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)
956{
957    context_t *text = (context_t *) conn_context;
958    KTEXT_ST authent;
959    int ret;
960
961    *clientout = NULL;
962    *clientoutlen = 0;
963
964    authent.length = MAX_KTXT_LEN;
965
966    switch (text->state) {
967
968    case 1: {
969	/* We should've just recieved a 32-bit number in network byte order.
970	 * We want to reply with an authenticator. */
971	int result;
972	KTEXT_ST ticket;
973	char *dot;
974
975	memset(&ticket, 0L, sizeof(ticket));
976	ticket.length = MAX_KTXT_LEN;
977
978	if (serverinlen != 4) {
979	    text->utils->seterror(text->utils->conn, 0,
980				  "server challenge not 4 bytes long");
981	    return SASL_BADPROT;
982	}
983
984	memcpy(&text->challenge, serverin, 4);
985
986	text->challenge=ntohl(text->challenge);
987
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;
993	}
994	if (cparams->service == NULL) {
995	    cparams->utils->log(NULL, SASL_LOG_ERR,
996				"no 'service' set");
997	    SETERROR(text->utils, "paramater error");
998	    return SASL_BADPARAM;
999	}
1000
1001	KRB_LOCK_MUTEX(cparams->utils);
1002	text->realm=krb_realmofhost(cparams->serverFQDN);
1003	text->hostname=(char *) cparams->serverFQDN;
1004
1005	/* the instance of the principal we're authenticating with */
1006	strncpy (text->instance, krb_get_phost (cparams->serverFQDN),
1007		 sizeof (text->instance));
1008
1009	/* text->instance is NULL terminated unless it was too long */
1010	text->instance[sizeof(text->instance)-1] = '\0';
1011
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
1015	 */
1016	dot = strchr(text->instance, '.');
1017	if (dot) *dot = '\0';
1018
1019#ifndef macintosh
1020	if ((result = krb_mk_req(&ticket, (char *) cparams->service,
1021				 text->instance, text->realm, text->challenge)))
1022#else
1023	    memset(&text->credentials,0,sizeof(text->credentials));
1024	if (kcglue_krb_mk_req(ticket.dat,
1025			      &ticket.length,
1026			      cparams->service,
1027			      text->instance,
1028			      text->realm,
1029			      text->challenge,
1030			      &text->credentials.session,
1031			      text->credentials.pname,
1032			      text->credentials.pinst) != 0)
1033#endif
1034	    {
1035		KRB_UNLOCK_MUTEX(cparams->utils);
1036
1037		text->utils->seterror(text->utils->conn,SASL_NOLOG,
1038				      "krb_mk_req() failed");
1039
1040		cparams->utils->log(NULL, SASL_LOG_ERR,
1041				    "krb_mk_req() failed: %s (%d)",
1042				    get_krb_err_txt(result), result);
1043		return SASL_FAIL;
1044	    }
1045
1046	KRB_UNLOCK_MUTEX(cparams->utils);
1047
1048	ret = _plug_buf_alloc(text->utils, &(text->out_buf),
1049			      &(text->out_buf_len), ticket.length);
1050	if (ret != SASL_OK) return ret;
1051
1052	memcpy(text->out_buf, ticket.dat, ticket.length);
1053
1054	*clientout = text->out_buf;
1055	*clientoutlen = ticket.length;
1056
1057	text->state = 2;
1058
1059	return SASL_CONTINUE;
1060    }
1061
1062    /* challenge #2 */
1063    case 2: {
1064	int need = 0;
1065	int musthave = 0;
1066	int testnum;
1067	int nchal;
1068	unsigned char *sout = NULL;
1069	unsigned len;
1070	unsigned char in[8];
1071	int result;
1072	int servermaxbuf;
1073	char *buf;
1074	int user_result = SASL_OK;
1075
1076	/* try to get the authid */
1077	if (text->user == NULL) {
1078	    user_result = _plug_get_userid(cparams->utils, &text->user,
1079					   prompt_need);
1080
1081	    if (user_result != SASL_OK && user_result != SASL_INTERACT)
1082		return user_result;
1083	}
1084
1085	/* free prompts we got */
1086	if (prompt_need && *prompt_need) {
1087	    cparams->utils->free(*prompt_need);
1088	    *prompt_need = NULL;
1089	}
1090
1091	/* if there are prompts not filled in */
1092	if (user_result == SASL_INTERACT) {
1093	    /* make the prompt list */
1094	    int result =
1095		_plug_make_prompts(cparams->utils, prompt_need,
1096				   user_result == SASL_INTERACT ?
1097				   "Please enter your authorization name" : NULL, NULL,
1098				   NULL, NULL,
1099				   NULL, NULL,
1100				   NULL, NULL, NULL,
1101				   NULL, NULL, NULL);
1102	    if (result!=SASL_OK) return result;
1103
1104	    return SASL_INTERACT;
1105	}
1106
1107	/* must be 8 octets */
1108	if (serverinlen!=8) {
1109	    SETERROR(cparams->utils,
1110		     "server response not 8 bytes long");
1111	    return SASL_BADAUTH;
1112	}
1113
1114	memcpy(in, serverin, 8);
1115
1116#ifndef macintosh
1117	/* get credentials */
1118	KRB_LOCK_MUTEX(cparams->utils);
1119	result = krb_get_cred((char *)cparams->service,
1120			      text->instance,
1121			      text->realm,
1122			      &text->credentials);
1123	KRB_UNLOCK_MUTEX(cparams->utils);
1124
1125	if(result != 0) {
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;
1131	}
1132#endif
1133	memcpy(text->session, text->credentials.session, 8);
1134
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);
1139
1140	/* decrypt from server */
1141	des_ecb_encrypt((des_cblock *)in, (des_cblock *)in,
1142			text->init_keysched, DES_DECRYPT);
1143
1144	/* convert to 32bit int */
1145	testnum = (in[0]*256*256*256)+(in[1]*256*256)+(in[2]*256)+in[3];
1146
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;
1151	}
1152
1153	/* construct 8 octets
1154	 * 1-4 - original checksum
1155	 * 5 - bitmask of sec layer
1156	 * 6-8 max buffer size
1157	 */
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;
1167	}
1168
1169	/* create stuff to send to server */
1170	sout = (char *)
1171	    cparams->utils->malloc(9+(text->user ? strlen(text->user) : 0)+9);
1172	if (!sout) {
1173	    MEMERROR(cparams->utils);
1174	    return SASL_NOMEM;
1175	}
1176
1177	nchal = htonl(text->challenge);
1178	memcpy(sout, &nchal, 4);
1179
1180	/* need bits of layer */
1181	if(cparams->props.maxbufsize == 0) {
1182	    need = musthave = 0;
1183	} else {
1184	    need = cparams->props.max_ssf - cparams->external_ssf;
1185	    musthave = cparams->props.min_ssf - cparams->external_ssf;
1186	}
1187
1188	oparams->decode = &kerberosv4_decode;
1189	oparams->encode = &kerberosv4_encode;
1190
1191	if ((in[4] & KRB_SECFLAG_ENCRYPTION)
1192	    && (need>=56) && (musthave <= 56)) {
1193	    /* encryption */
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)) {
1200	    /* integrity */
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)) {
1206	    /* no layer */
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;
1212	} else {
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;
1218	}
1219
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;
1225	}
1226
1227	if(cparams->props.maxbufsize) {
1228	    int tmpmaxbuf = ( cparams->props.maxbufsize > 0xFFFFFF ) ? 0xFFFFFF : cparams->props.maxbufsize;
1229
1230	    sout[5]=((tmpmaxbuf >> 16) & 0xFF);
1231	    sout[6]=((tmpmaxbuf >> 8) & 0xFF);
1232	    sout[7]=(tmpmaxbuf & 0xFF);
1233	} else {
1234            /* let's say we can support up to 64K */
1235	    /* no inherent inability with our layers to support more */
1236
1237	    sout[5]=0x00;  /* max ciphertext buffer size */
1238	    sout[6]=0xFF;
1239	    sout[7]=0xFF;
1240	}
1241
1242	sout[8] = 0x00; /* just to be safe */
1243
1244	/* append userid */
1245	len = 9;			/* 8 + trailing NULL */
1246	if (text->user) {
1247	    strcpy((char *)sout + 8, text->user);
1248	    len += strlen(text->user);
1249	}
1250
1251	/* append 0 based octets so is multiple of 8 */
1252	while(len % 8) {
1253	    sout[len]=0;
1254	    len++;
1255	}
1256	sout[len]=0;
1257
1258	des_pcbc_encrypt((des_cblock *)sout,
1259			 (des_cblock *)sout,
1260			 len,
1261			 text->init_keysched,
1262			 (des_cblock *)text->session,
1263			 DES_ENCRYPT);
1264
1265	result = _plug_buf_alloc(text->utils, &text->out_buf,
1266				 &text->out_buf_len, len);
1267	if (result != SASL_OK)  return result;
1268
1269	memcpy(text->out_buf, sout, len);
1270
1271	*clientout = text->out_buf;
1272	*clientoutlen = len;
1273
1274	/* nothing more to do; should be authenticated */
1275	if(cparams->iplocalport) {
1276	    result = ipv4_ipfromstring(cparams->utils,
1277				       cparams->iplocalport,
1278				       &(text->ip_local));
1279	    if (result != SASL_OK) {
1280		/* couldn't get local IP address */
1281		return result;
1282	    }
1283	}
1284
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 */
1291		return result;
1292	    }
1293	}
1294
1295	buf = cparams->utils->malloc(strlen(text->credentials.pname)
1296				     + strlen(text->credentials.pinst)
1297				     + 2);
1298	if (!buf) {
1299	    MEMERROR(cparams->utils);
1300	    return SASL_NOMEM;
1301	}
1302	strcpy(buf, text->credentials.pname);
1303	if (text->credentials.pinst[0]) {
1304	    strcat(buf, ".");
1305	    strcat(buf, text->credentials.pinst);
1306	}
1307
1308	if (text->user && !text->user[0]) {
1309	    text->user = NULL;
1310	}
1311
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);
1317	    return ret;
1318	}
1319
1320	if (!text->user) {
1321	    /* 0 in length fields means use strlen() */
1322	    ret = cparams->canon_user(cparams->utils->conn, buf, 0,
1323				      SASL_CU_AUTHZID, oparams);
1324	} else {
1325	    ret = cparams->canon_user(cparams->utils->conn, text->user, 0,
1326				      SASL_CU_AUTHZID, oparams);
1327	}
1328
1329	cparams->utils->free(buf);
1330
1331	oparams->doneflag = 1;
1332	oparams->param_version = 0;
1333
1334	/* used by layers */
1335	_plug_decode_init(&text->decode_context, text->utils,
1336			  (cparams->props.maxbufsize > 0xFFFFFF) ? 0xFFFFFF :
1337			  cparams->props.maxbufsize);
1338
1339	if (sout) cparams->utils->free(sout);
1340
1341	return SASL_OK;
1342    }
1343
1344    default:
1345	cparams->utils->log(NULL, SASL_LOG_ERR,
1346			    "Invalid Kerberos client step %d\n", text->state);
1347	return SASL_FAIL;
1348    }
1349
1350    return SASL_FAIL; /* should never get here */
1351}
1352
1353static const long kerberosv4_required_prompts[] = {
1354    SASL_CB_LIST_END
1355};
1356
1357static sasl_client_plug_t kerberosv4_client_plugins[] =
1358{
1359    {
1360	"KERBEROS_V4",			/* mech_name */
1361	KRB_DES_SECURITY_BITS,		/* max_ssf */
1362	SASL_SEC_NOPLAINTEXT
1363	| SASL_SEC_NOACTIVE
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 */
1375	NULL,				/* idle */
1376	NULL,				/* spare */
1377	NULL				/* spare */
1378    }
1379};
1380
1381int kerberos4_client_plug_init(const sasl_utils_t *utils,
1382			       int maxversion,
1383			       int *out_version,
1384			       sasl_client_plug_t **pluglist,
1385			       int *plugcount)
1386{
1387    if (maxversion < SASL_CLIENT_PLUG_VERSION) {
1388	SETERROR(utils, "Wrong KERBEROS_V4 version");
1389	return SASL_BADVERS;
1390    }
1391
1392    if(!krb_mutex) {
1393	krb_mutex = utils->mutex_alloc();
1394	if(!krb_mutex) {
1395	    return SASL_FAIL;
1396	}
1397    }
1398
1399    *out_version = SASL_CLIENT_PLUG_VERSION;
1400    *pluglist = kerberosv4_client_plugins;
1401    *plugcount = 1;
1402
1403    refcount++;
1404
1405    return SASL_OK;
1406}
1407