GSS_S_PROMPTING_NEEDED is a bit
[cyrus-sasl.git] / plugins / kerberos4.c
1 /* Kerberos4 SASL plugin
2  * Rob Siemborski
3  * Tim Martin 
4  * $Id: kerberos4.c,v 1.99 2005/01/10 07:08:53 shadow 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 */
97 typedef 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 */
113 extern int gethostname(char *, int);
114 #endif
115
116 /*****************************  Common Section  *****************************/
117
118 static const char plugin_id[] = "$Id: kerberos4.c,v 1.99 2005/01/10 07:08:53 shadow 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
133 typedef enum Krb_sec {
134     KRB_SEC_NONE = 0,
135     KRB_SEC_INTEGRITY = 1,
136     KRB_SEC_ENCRYPTION = 2
137 } Krb_sec_t;
138
139 typedef 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 */
208 static void *krb_mutex = NULL;
209 static char *srvtab = NULL;
210 static unsigned refcount = 0;
211
212 static 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
272 static 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
333 static 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
349 static 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
368 static 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
389 static void
390 kerberosv4_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
406 static 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
443 static 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
464 static int
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)),
469                            void **conn_context)
470 {
471     return new_text(sparams->utils, (context_t **) conn_context);
472 }
473
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)
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;
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
830 static 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
850 static 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
875 int 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
940 static int
941 kerberosv4_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
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)
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
1353 static const long kerberosv4_required_prompts[] = {
1354     SASL_CB_LIST_END
1355 };
1356
1357 static 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
1381 int 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 }