1 /* MODULE: auth_krb5 */
4 * Copyright (c) 1997 Messaging Direct Ltd.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
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
31 #ident "$Id: auth_krb5.c,v 1.17 2005/02/14 05:50:49 shadow Exp $"
34 /* ok, this is wrong but the most convenient way of doing
35 * it for now. We assume (possibly incorrectly) that if GSSAPI exists then
36 * the Kerberos 5 headers and libraries exist.
37 * What really should be done is a configure.in check for krb5.h and use
38 * that since none of this code is GSSAPI but rather raw Kerberos5.
42 /* Also, at some point one would hope it would be possible to
43 * have less divergence between Heimdal and MIT Kerberos 5.
45 * As of the summer of 2003, the obvious issues are that
46 * MIT doesn't have krb5_verify_opt_*() and Heimdal doesn't
47 * have krb5_sname_to_principal().
50 /* PUBLIC DEPENDENCIES */
51 #include "mechanisms.h"
52 #include "globals.h" /* mech_option */
58 static cfile config = 0;
59 static char *keytabname = NULL; /* "system default" */
60 static char *verify_principal = "host"; /* a principal in the default keytab */
61 #endif /* AUTH_KRB5 */
70 #include "auth_krb5.h"
72 /* END PUBLIC DEPENDENCIES */
74 int /* R: -1 on failure, else 0 */
77 void /* no parameters */
85 if (krbtf_init() == -1) {
86 syslog(LOG_ERR, "auth_krb5_init krbtf_init failed");
91 configname = mech_option;
92 else if (access(SASLAUTHD_CONF_FILE_DEFAULT, F_OK) == 0)
93 configname = SASLAUTHD_CONF_FILE_DEFAULT;
98 if (!(config = cfile_read(configname, complaint, sizeof (complaint)))) {
99 syslog(LOG_ERR, "auth_krb5_init %s", complaint);
105 keytabname = cfile_getstring(config, "krb5_keytab", keytabname);
106 verify_principal = cfile_getstring(config, "krb5_verify_principal", verify_principal);
119 form_principal_name (
127 const char *forced_instance = 0;
133 snprintf(keyname, sizeof (keyname), "krb5_%s_instance", service);
134 forced_instance = cfile_getstring(config, keyname, 0);
137 if (forced_instance) {
138 char *user_specified;
140 if (user_specified = strchr(user, '/')) {
141 if (strcmp(user_specified + 1, forced_instance)) {
142 /* user not allowed to override sysadmin */
145 /* don't need to force--user already asked for it */
151 /* form user[/instance][@realm] */
152 plen = snprintf(pname, pnamelen, "%s%s%s%s%s",
154 (forced_instance ? "/" : ""),
155 (forced_instance ? forced_instance : ""),
156 ((realm && realm[0]) ? "@" : ""),
157 ((realm && realm[0]) ? realm : "")
159 if ((plen <= 0) || (plen >= pnamelen))
162 /* Perhaps we should uppercase the realm? */
169 char * /* R: allocated response string */
172 const char *user, /* I: plaintext authenticator */
173 const char *password, /* I: plaintext password */
174 const char *service, /* I: service authenticating to */
175 const char *realm /* I: user's realm */
180 krb5_context context;
181 krb5_ccache ccache = NULL;
182 krb5_keytab kt = NULL;
183 krb5_principal auth_user;
187 char principalbuf[2048];
190 if (!user || !password) {
191 syslog(LOG_ERR, "auth_krb5: NULL password or username?");
192 return strdup("NO saslauthd internal NULL password or username");
195 if (krb5_init_context(&context)) {
196 syslog(LOG_ERR, "auth_krb5: krb5_init_context");
197 return strdup("NO saslauthd internal krb5_init_context error");
200 if (form_principal_name(user, service, realm, principalbuf, sizeof (principalbuf))) {
201 syslog(LOG_ERR, "auth_krb5: form_principal_name");
202 return strdup("NO saslauthd principal name error");
205 if (krb5_parse_name (context, principalbuf, &auth_user)) {
206 krb5_free_context(context);
207 syslog(LOG_ERR, "auth_krb5: krb5_parse_name");
208 return strdup("NO saslauthd internal krb5_parse_name error");
211 if (krbtf_name(tfname, sizeof (tfname)) != 0) {
212 syslog(LOG_ERR, "auth_krb5: could not generate ccache name");
213 return strdup("NO saslauthd internal error");
216 if (krb5_cc_resolve(context, tfname, &ccache)) {
217 krb5_free_principal(context, auth_user);
218 krb5_free_context(context);
219 syslog(LOG_ERR, "auth_krb5: krb5_cc_resolve");
220 return strdup("NO saslauthd internal error");
224 if (krb5_kt_resolve(context, keytabname, &kt)) {
225 krb5_free_principal(context, auth_user);
226 krb5_cc_destroy(context, ccache);
227 krb5_free_context(context);
228 syslog(LOG_ERR, "auth_krb5: krb5_kt_resolve");
229 return strdup("NO saslauthd internal error");
233 krb5_verify_opt_init(&opt);
234 krb5_verify_opt_set_secure(&opt, 1);
235 krb5_verify_opt_set_ccache(&opt, ccache);
237 krb5_verify_opt_set_keytab(&opt, kt);
238 krb5_verify_opt_set_service(&opt, verify_principal);
240 if (krb5_verify_user_opt(context, auth_user, password, &opt)) {
241 result = strdup("NO krb5_verify_user_opt failed");
243 result = strdup("OK");
246 krb5_free_principal(context, auth_user);
247 krb5_cc_destroy(context, ccache);
249 krb5_kt_close(context, kt);
250 krb5_free_context(context);
255 #else /* !KRB5_HEIMDAL */
257 /* returns 0 for failure, 1 for success */
258 static int k5support_verify_tgt(krb5_context context,
261 krb5_principal server;
263 krb5_keyblock *keyblock = NULL;
264 krb5_auth_context auth_context = NULL;
265 krb5_error_code k5_retcode;
266 krb5_keytab kt = NULL;
267 char thishost[BUFSIZ];
270 memset(&packet, 0, sizeof(packet));
272 if (krb5_sname_to_principal(context, NULL, verify_principal,
273 KRB5_NT_SRV_HST, &server)) {
278 if (krb5_kt_resolve(context, keytabname, &kt)) {
283 if (krb5_kt_read_service_key(context, kt, server, 0,
289 krb5_free_keyblock(context, keyblock);
292 /* this duplicates work done in krb5_sname_to_principal
295 if (gethostname(thishost, BUFSIZ) < 0) {
298 thishost[BUFSIZ-1] = '\0';
300 k5_retcode = krb5_mk_req(context, &auth_context, 0, verify_principal,
301 thishost, NULL, ccache, &packet);
304 krb5_auth_con_free(context, auth_context);
312 if (krb5_rd_req(context, &auth_context, &packet,
313 server, NULL, NULL, NULL)) {
318 krb5_auth_con_free(context, auth_context);
322 /* all is good now */
325 krb5_free_data_contents(context, &packet);
326 krb5_free_principal(context, server);
331 /* FUNCTION: auth_krb5 */
334 * Authenticate against Kerberos V.
337 char * /* R: allocated response string */
340 const char *user, /* I: plaintext authenticator */
341 const char *password, /* I: plaintext password */
342 const char *service, /* I: service authenticating to */
343 const char *realm /* I: user's realm */
348 krb5_context context;
349 krb5_ccache ccache = NULL;
350 krb5_principal auth_user;
352 krb5_get_init_creds_opt opts;
355 char principalbuf[2048];
356 krb5_error_code code;
359 if (!user|| !password) {
360 syslog(LOG_ERR, "auth_krb5: NULL password or username?");
361 return strdup("NO saslauthd internal error");
364 if (krb5_init_context(&context)) {
365 syslog(LOG_ERR, "auth_krb5: krb5_init_context");
366 return strdup("NO saslauthd internal error");
369 if (form_principal_name(user, service, realm, principalbuf, sizeof (principalbuf))) {
370 syslog(LOG_ERR, "auth_krb5: form_principal_name");
371 return strdup("NO saslauthd principal name error");
374 if (krb5_parse_name (context, principalbuf, &auth_user)) {
375 krb5_free_context(context);
376 syslog(LOG_ERR, "auth_krb5: krb5_parse_name");
377 return strdup("NO saslauthd internal error");
380 if (krbtf_name(tfname, sizeof (tfname)) != 0) {
381 syslog(LOG_ERR, "auth_krb5: could not generate ticket file name");
382 return strdup("NO saslauthd internal error");
385 if (krb5_cc_resolve(context, tfname, &ccache)) {
386 krb5_free_principal(context, auth_user);
387 krb5_free_context(context);
388 syslog(LOG_ERR, "auth_krb5: krb5_cc_resolve");
389 return strdup("NO saslauthd internal error");
392 if (krb5_cc_initialize (context, ccache, auth_user)) {
393 krb5_free_principal(context, auth_user);
394 krb5_free_context(context);
395 syslog(LOG_ERR, "auth_krb5: krb5_cc_initialize");
396 return strdup("NO saslauthd internal error");
399 krb5_get_init_creds_opt_init(&opts);
400 /* 15 min should be more than enough */
401 krb5_get_init_creds_opt_set_tkt_life(&opts, 900);
402 if (code = krb5_get_init_creds_password(context, &creds,
403 auth_user, password, NULL, NULL,
405 krb5_cc_destroy(context, ccache);
406 krb5_free_principal(context, auth_user);
407 krb5_free_context(context);
408 syslog(LOG_ERR, "auth_krb5: krb5_get_init_creds_password: %d", code);
409 return strdup("NO saslauthd internal error");
412 /* at this point we should have a TGT. Let's make sure it is valid */
413 if (krb5_cc_store_cred(context, ccache, &creds)) {
414 krb5_free_principal(context, auth_user);
415 krb5_cc_destroy(context, ccache);
416 krb5_free_context(context);
417 syslog(LOG_ERR, "auth_krb5: krb5_cc_store_cred");
418 return strdup("NO saslauthd internal error");
421 if (!k5support_verify_tgt(context, ccache)) {
422 syslog(LOG_ERR, "auth_krb5: k5support_verify_tgt");
423 result = strdup("NO saslauthd internal error");
428 * fall through -- user is valid beyond this point
431 result = strdup("OK");
433 /* destroy any tickets we had */
434 krb5_free_cred_contents(context, &creds);
435 krb5_free_principal(context, auth_user);
436 krb5_cc_destroy(context, ccache);
437 krb5_free_context(context);
442 #endif /* KRB5_HEIMDAL */
444 #else /* ! AUTH_KRB5 */
448 const char *login __attribute__((unused)),
449 const char *password __attribute__((unused)),
450 const char *service __attribute__((unused)),
451 const char *realm __attribute__((unused))
457 #endif /* ! AUTH_KRB5 */
459 /* END FUNCTION: auth_krb5 */
461 /* END MODULE: auth_krb5 */