GSS_S_PROMPTING_NEEDED is a bit
[cyrus-sasl.git] / saslauthd / auth_krb4.c
1 /* MODULE: auth_krb4 */
2
3 /* COPYRIGHT
4  * Copyright (c) 1997 Messaging Direct Ltd.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY MESSAGING DIRECT LTD. ``AS IS'' AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL MESSAGING DIRECT LTD. OR
20  * ITS EMPLOYEES OR AGENTS BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
22  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
23  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
25  * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
26  * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
27  * DAMAGE.
28  * END COPYRIGHT */
29
30 #ifdef __GNUC__
31 #ident "$Id: auth_krb4.c,v 1.12 2005/02/01 12:26:34 mel Exp $"
32 #endif
33
34 /* PUBLIC DEPENDENCIES */
35 #include <unistd.h>
36 #include "mechanisms.h"
37 #include "globals.h"
38 #include "cfile.h"
39 #include "krbtf.h"
40
41 #ifdef AUTH_KRB4
42
43 # include <krb.h>
44
45 # ifdef WITH_DES
46 #  ifdef WITH_SSL_DES
47 #   include <openssl/des.h>
48 #  else
49 #   include <des.h>
50 #  endif /* WITH_SSL_DES */
51 # endif /* WITH_DES */
52
53 #endif /* AUTH_KRB4 */
54
55 #include <errno.h>
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <string.h>
59 #include <syslog.h>
60 #include <sys/stat.h>
61 #include "auth_krb4.h"
62
63 #ifdef DEADCODE
64 extern int swap_bytes;                  /* from libkrb.a   */
65 #endif /* DEADCODE */
66 /* END PUBLIC DEPENDENCIES */
67
68 /* PRIVATE DEPENDENCIES */
69 #ifdef AUTH_KRB4
70 static char default_realm[REALM_SZ];
71 static cfile config = 0;
72 static char myhostname[BUFSIZ];    /* Is BUFSIZ right here? */
73 static char *srvtabname = "";      /* "" means "system default srvtab" */
74 static char *verify_principal = "rcmd"; /* A principal in the default srvtab */
75 #endif /* AUTH_KRB4 */
76 /* END PRIVATE DEPENDENCIES */
77
78 #define TF_NAME_LEN 128
79
80 /* Kerberos for Macintosh doesn't define this, so we will. (Thanks Fink!) */
81 #ifndef KRB_TICKET_GRANTING_TICKET
82 #define KRB_TICKET_GRANTING_TICKET "krbtgt"
83 #endif /* !defined(KRB_TICKET_GRANTING_TICKET) */
84
85 \f
86 /* FUNCTION: auth_krb4_init */
87
88 /* SYNOPSIS
89  * Initialize the Kerberos IV authentication environment.
90  *
91  * krb4 proxy authentication has a side effect of creating a ticket
92  * file for the user we are authenticating. We keep these in a private
93  * directory so as not to override a system ticket file that may be
94  * in use.
95  *
96  * This function tries to create the directory, and initializes the
97  * global variable tf_dir with the pathname of the directory.
98  * END SYNOPSIS */
99
100 int                                     /* R: -1 on failure, else 0 */
101 auth_krb4_init (
102   /* PARAMETERS */
103   void                                  /* no parameters */
104   /* END PARAMETERS */
105   )
106 {
107 #ifdef AUTH_KRB4
108     /* VARIABLES */
109     int rc;                             /* return code holder */
110     char *configname = 0;
111     /* END VARIABLES */
112
113     if (mech_option)
114       configname = mech_option;
115     else if (access(SASLAUTHD_CONF_FILE_DEFAULT, F_OK) == 0)
116       configname = SASLAUTHD_CONF_FILE_DEFAULT;
117
118     if (configname) {
119       char complaint[1024];
120
121       config = cfile_read(configname, complaint, sizeof(complaint));
122       if (!config) {
123         syslog(LOG_ERR, "auth_krb4_init %s", complaint);
124         return -1;
125       }
126     }
127
128     if (config) {
129       srvtabname = cfile_getstring(config, "krb4_srvtab", srvtabname);
130       verify_principal = cfile_getstring(config, "krb4_verify_principal",
131                                          verify_principal);
132     }
133     
134     if (krbtf_init() == -1) {
135       syslog(LOG_ERR, "auth_krb4_init krbtf_init failed");
136       return -1;
137     }
138
139     rc = krb_get_lrealm(default_realm, 1);
140     if (rc) {
141         syslog(LOG_ERR, "auth_krb4: krb_get_lrealm: %s",
142                krb_get_err_text(rc));
143         return -1;
144     }
145
146     if (gethostname(myhostname, sizeof(myhostname)) < 0) {
147       syslog(LOG_ERR, "auth_krb4: gethoanem(): %m");
148       return -1;
149     }
150     myhostname[sizeof(myhostname) - 1] = '\0';
151
152     return 0;
153 #else /* ! AUTH_KRB4 */
154     return -1;
155 #endif /* ! AUTH_KRB4 */
156 }
157
158 /* END FUNCTION: auth_krb4_init */
159 \f
160 /* FUNCTION: auth_krb4 */
161
162 /* SYNOPSIS
163  * Authenticate against Kerberos IV.
164  * END SYNOPSIS */
165
166 #ifdef AUTH_KRB4
167
168 char *                                  /* R: allocated response string */
169 auth_krb4 (
170   /* PARAMETERS */
171   const char *login,                    /* I: plaintext authenticator */
172   const char *password,                 /* I: plaintext password */
173   const char *service,
174   const char *realm_in
175   /* END PARAMETERS */
176   )
177 {
178     /* VARIABLES */
179     char aname[ANAME_SZ];               /* Kerberos principal */
180     const char *realm;                  /* Kerberos realm to authenticate in */
181     int rc;                             /* return code */
182     char tf_name[TF_NAME_LEN];          /* Ticket file name */
183     char *instance, *user_specified;
184     KTEXT_ST ticket;
185     AUTH_DAT kdata;
186     /* END VARIABLES */
187
188     /*
189      * Make sure we have a password. If this is NULL the call
190      * to krb_get_pw_in_tkt below would try to prompt for
191      * one interactively.
192      */
193     if (password == NULL) {
194         syslog(LOG_ERR, "auth_krb4: NULL password?");
195         return strdup("NO saslauthd internal error");
196     }
197
198     if (krbtf_name(tf_name, sizeof(tf_name)) != 0) {
199       syslog(LOG_ERR, "auth_krb4: could not generate ticket file name");
200       return strdup("NO saslauthd internal error");
201     }
202     krb_set_tkt_string(tf_name);
203
204     strncpy(aname, login, ANAME_SZ-1);
205     aname[ANAME_SZ-1] = '\0';
206
207     instance = "";
208
209     if (config) {
210       char keyname[1024];
211
212       snprintf(keyname, sizeof(keyname), "krb4_%s_instance", service);
213       instance = cfile_getstring(config, keyname, "");
214     }
215
216     user_specified = strchr(aname, '.');
217     if (user_specified) {
218       if (instance && instance[0]) {
219         /* sysadmin specified a (mandatory) instance */
220         if (strcmp(user_specified + 1, instance)) {
221           return strdup("NO saslauthd principal name error");
222         }
223         /* nuke instance from "aname"-- matches what's already in "instance" */
224         *user_specified = '\0';
225       } else {
226         /* sysadmin has no preference, so we shift
227          * instance name from "aname" to "instance"
228          */
229         *user_specified = '\0';
230         instance = user_specified + 1;
231       }
232     }
233
234     if(realm_in && *realm_in != '\0') {
235         realm = realm_in;
236     } else {
237         realm = default_realm;
238     }
239
240     rc = krb_get_pw_in_tkt(aname, instance, realm,
241                            KRB_TICKET_GRANTING_TICKET,
242                            realm, 1, password);
243
244     if (rc == INTK_BADPW || rc == KDC_PR_UNKNOWN) {
245         return strdup("NO");
246     } else if (rc != KSUCCESS) {
247       syslog(LOG_ERR, "ERROR: auth_krb4: krb_get_pw_in_tkt: %s",
248              krb_get_err_text(rc));
249
250       return strdup("NO saslauthd internal error");
251     }
252
253     /* if the TGT wasn't spoofed, it should entitle us to an rcmd ticket... */
254     rc = krb_mk_req(&ticket, verify_principal, myhostname, default_realm, 0);
255
256     if (rc != KSUCCESS) {
257       syslog(LOG_ERR, "ERROR: auth_krb4: krb_get_pw_in_tkt: %s",
258              krb_get_err_text(rc));
259       dest_tkt();
260       return strdup("NO saslauthd internal error");
261     }
262
263     /* .. and that ticket should match our secret host key */
264     rc = krb_rd_req(&ticket, verify_principal, myhostname, 0, &kdata, srvtabname);
265
266     if (rc != RD_AP_OK) {
267       syslog(LOG_ERR, "ERROR: auth_krb4: krb_rd_req:%s",
268              krb_get_err_text(rc));
269       dest_tkt();
270       return strdup("NO saslauthd internal error");
271     }
272
273     dest_tkt();
274
275     return strdup("OK");
276 }
277
278 #else /* ! AUTH_KRB4 */
279
280 char *
281 auth_krb4 (
282   const char *login __attribute__((unused)),
283   const char *password __attribute__((unused)),
284   const char *service __attribute__((unused)),
285   const char *realm __attribute__((unused))
286   )
287 {
288     return NULL;
289 }
290
291 #endif /* ! AUTH_KRB4 */
292
293 /* END FUNCTION: auth_krb4 */
294
295 /* END MODULE: auth_krb4 */