1 /* MODULE: auth_krb5 */
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 /* PUBLIC DEPENDENCIES */
31 #include "mechanisms.h"
32 #include "globals.h" /* mech_option */
33 #include "cfile.h"
34 
35 #ifdef AUTH_KRB5
36 # include <krb5.h>
37 static cfile config = 0;
38 static char *keytabname = NULL; /* "system default" */
39 static char *verify_principal = "host"; /* a principal in the default keytab */
40 static char *servername = NULL; /* server name to use in principal */
41 #endif /* AUTH_KRB5 */
42 
43 #include <errno.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <syslog.h>
48 #include <unistd.h>
49 #include <sys/stat.h>
50 #include "auth_krb5.h"
51 
52 /* END PUBLIC DEPENDENCIES */
53 
54 int					/* R: -1 on failure, else 0 */
auth_krb5_init(void)55 auth_krb5_init (
56   /* PARAMETERS */
57   void					/* no parameters */
58   /* END PARAMETERS */
59   )
60 {
61 #ifdef AUTH_KRB5
62     char *configname = 0;
63 
64     if (mech_option)
65 	configname = mech_option;
66     else if (access(SASLAUTHD_CONF_FILE_DEFAULT, F_OK) == 0)
67 	configname = SASLAUTHD_CONF_FILE_DEFAULT;
68 
69     if (configname) {
70 	char complaint[1024];
71 
72 	if (!(config = cfile_read(configname, complaint, sizeof (complaint)))) {
73 	    syslog(LOG_ERR, "auth_krb5_init %s", complaint);
74 	    return -1;
75 	}
76     }
77 
78     if (config) {
79 	keytabname = (char *) cfile_getstring(config, "krb5_keytab", keytabname);
80 	verify_principal = (char *) cfile_getstring(config, "krb5_verify_principal", verify_principal);
81 	servername = (char *) cfile_getstring(config, "krb5_servername", servername);
82     }
83 
84     return 0;
85 
86 #else
87     return -1;
88 #endif
89 }
90 
91 #ifdef AUTH_KRB5
92 
93 static int
form_principal_name(const char * user,const char * service,const char * realm,char * pname,int pnamelen)94 form_principal_name (
95   const char *user,
96   const char *service,
97   const char *realm,
98   char *pname,
99   int pnamelen
100   )
101 {
102     const char *forced_instance = 0;
103 	int plen;
104 
105     plen = strlcpy(pname, user, pnamelen);
106     user = pname;
107 
108     if (config && cfile_getswitch(config, "krb5_conv_krb4_instance", 0)) {
109        char *krb4_instance;
110 
111        if ((krb4_instance = strchr(pname, '.'))) *krb4_instance = '/';
112     }
113 
114     if (config) {
115 	char keyname[1024];
116 
117 	snprintf(keyname, sizeof (keyname), "krb5_%s_instance", service);
118 	forced_instance = cfile_getstring(config, keyname, 0);
119     }
120 
121     if (forced_instance) {
122 	char *user_specified;
123 
124 	if ((user_specified = strchr(user, '/'))) {
125 	    if (strcmp(user_specified + 1, forced_instance)) {
126 		/* user not allowed to override sysadmin */
127 		return -1;
128 	    } else {
129 		/* don't need to force--user already asked for it */
130 		forced_instance = 0;
131 	    }
132 	}
133     }
134 
135     /* form user[/instance][@realm] */
136     plen += snprintf(pname+plen, pnamelen-plen, "%s%s%s%s",
137 	(forced_instance ? "/" : ""),
138 	(forced_instance ? forced_instance : ""),
139 	((realm && realm[0]) ? "@" : ""),
140 	((realm && realm[0]) ? realm : "")
141 	);
142     if ((plen <= 0) || (plen >= pnamelen))
143 	return -1;
144 
145     /* Perhaps we should uppercase the realm? */
146 
147     return 0;
148 }
149 
k5support_log_err(int priority,krb5_context context,krb5_error_code code,char const * msg)150 static void k5support_log_err(int priority,
151                               krb5_context context,
152                               krb5_error_code code,
153                               char const *msg)
154 {
155     const char *k5_msg = krb5_get_error_message(context, code);
156 
157     syslog(priority, "auth_krb5: %s: %s (%d)\n", msg, k5_msg, code);
158     krb5_free_error_message(context, k5_msg);
159 }
160 
161 char *                                  /* R: allocated response string */
auth_krb5(const char * user,const char * password,const char * service,const char * realm)162 auth_krb5 (
163   /* PARAMETERS */
164   const char *user,                     /* I: plaintext authenticator */
165   const char *password,                 /* I: plaintext password */
166   const char *service,                  /* I: service authenticating to */
167   const char *realm                     /* I: user's realm */
168   /* END PARAMETERS */
169   )
170 {
171     /* VARIABLES */
172     krb5_context context;
173     krb5_error_code rc;
174     krb5_keytab kt = NULL;
175     krb5_principal auth_user;
176     krb5_principal server;
177     krb5_get_init_creds_opt *opt;
178     krb5_verify_init_creds_opt vopt;
179     krb5_creds cred;
180     char * result;
181     char principalbuf[2048];
182     /* END VARIABLES */
183 
184     if (!user || !password) {
185         syslog(LOG_ERR, "auth_krb5: NULL password or username?");
186         return strdup("NO saslauthd NULL password or username");
187     }
188 
189     if (krb5_init_context(&context)) {
190         syslog(LOG_ERR, "auth_krb5: krb5_init_context");
191         return strdup("NO saslauthd internal error");
192     }
193 
194     if (form_principal_name(user, service, realm, principalbuf, sizeof (principalbuf))) {
195         syslog(LOG_ERR, "auth_krb5: form_principal_name");
196         return strdup("NO saslauthd principal name error");
197     }
198 
199     if ((rc = krb5_parse_name(context, principalbuf, &auth_user))) {
200         k5support_log_err(LOG_ERR, context, rc, "krb5_parse_name");
201         krb5_free_context(context);
202         return strdup("NO saslauthd internal error");
203     }
204 
205     if ((rc = krb5_get_init_creds_opt_alloc(context, &opt))) {
206         k5support_log_err(LOG_ERR, context, rc, "krb5_get_init_creds_opt_alloc");
207         krb5_free_principal(context, auth_user);
208         krb5_free_context(context);
209         return strdup("NO saslauthd internal error");
210     }
211 
212 #ifdef KRB5_HEIMDAL
213     krb5_get_init_creds_opt_set_default_flags(context, NULL,
214                                               krb5_principal_get_realm(context, auth_user),
215                                               opt);
216 #endif /* KRB5_HEIMDAL */
217 
218     rc = krb5_get_init_creds_password(context, &cred, auth_user, password, NULL,
219                                  NULL, 0, NULL, opt);
220     krb5_get_init_creds_opt_free(context, opt);
221     if (rc) {
222         k5support_log_err(LOG_ERR, context, rc, "krb5_get_init_creds_password");
223         krb5_free_principal(context, auth_user);
224         krb5_free_context(context);
225         return strdup("NO krb5_get_init_creds_password failed");
226     }
227 
228     if (keytabname) {
229         if ((rc = krb5_kt_resolve(context, keytabname, &kt))) {
230             k5support_log_err(LOG_DEBUG, context, rc, "krb5_kt_resolve");
231             krb5_free_principal(context, auth_user);
232             krb5_free_cred_contents(context, &cred);
233             krb5_free_context(context);
234             return strdup("NO saslauthd internal error");
235         }
236     }
237 
238     if ((rc = krb5_sname_to_principal(context, servername, verify_principal,
239                                 KRB5_NT_SRV_HST, &server))) {
240         k5support_log_err(LOG_DEBUG, context, rc, "krb5_sname_to_principal");
241         krb5_free_principal(context, auth_user);
242         krb5_free_cred_contents(context, &cred);
243         if (kt) {
244             krb5_kt_close(context, kt);
245         }
246         krb5_free_context(context);
247         return strdup("NO saslauthd internal error");
248     }
249 
250     krb5_verify_init_creds_opt_init(&vopt);
251     krb5_verify_init_creds_opt_set_ap_req_nofail(&vopt, 1);
252 
253     if ((rc = krb5_verify_init_creds(context, &cred, server, kt, NULL, &vopt))) {
254         result = strdup("NO krb5_verify_init_creds failed");
255         k5support_log_err(LOG_ERR, context, rc, "krb5_verify_init_creds");
256     } else {
257         result = strdup("OK");
258     }
259 
260     krb5_free_principal(context, auth_user);
261     krb5_free_principal(context, server);
262     krb5_free_cred_contents(context, &cred);
263     if (kt) {
264         krb5_kt_close(context, kt);
265     }
266     krb5_free_context(context);
267 
268     return result;
269 }
270 
271 #else /* ! AUTH_KRB5 */
272 
273 char *
auth_krb5(const char * login,const char * password,const char * service,const char * realm)274 auth_krb5 (
275   const char *login __attribute__((unused)),
276   const char *password __attribute__((unused)),
277   const char *service __attribute__((unused)),
278   const char *realm __attribute__((unused))
279   )
280 {
281     return NULL;
282 }
283 
284 #endif /* ! AUTH_KRB5 */
285 
286 /* END FUNCTION: auth_krb5 */
287 
288 /* END MODULE: auth_krb5 */
289