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