1 /* auth_krb.c -- Kerberos authorization
2  *
3  * Copyright (c) 1994-2008 Carnegie Mellon University.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in
14  *    the documentation and/or other materials provided with the
15  *    distribution.
16  *
17  * 3. The name "Carnegie Mellon University" must not be used to
18  *    endorse or promote products derived from this software without
19  *    prior written permission. For permission or any legal
20  *    details, please contact
21  *      Carnegie Mellon University
22  *      Center for Technology Transfer and Enterprise Creation
23  *      4615 Forbes Avenue
24  *      Suite 302
25  *      Pittsburgh, PA  15213
26  *      (412) 268-7393, fax: (412) 268-7395
27  *      innovation@andrew.cmu.edu
28  *
29  * 4. Redistributions of any form whatsoever must retain the following
30  *    acknowledgment:
31  *    "This product includes software developed by Computing Services
32  *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
33  *
34  * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
35  * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
36  * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
37  * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
38  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
39  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
40  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
41  */
42 
43 #include <config.h>
44 #include <stdlib.h>
45 #include <sysexits.h>
46 
47 #include "auth.h"
48 #include "xmalloc.h"
49 #include "util.h"
50 
51 #ifdef HAVE_KRB
52 
53 #include <limits.h>
54 #include <stdio.h>
55 #include <ctype.h>
56 #include <string.h>
57 #include <sys/types.h>
58 
59 #include <krb.h>
60 #ifdef HAVE_BDB
61 #ifdef HAVE_DB_185_H
62 #include <db_185.h>
63 #else
64 #include <db.h>
65 #endif
66 #else
67 #include <ndbm.h>
68 #endif
69 #include <krb.h>
70 
71 #ifndef KRB_MAPNAME
72 #define KRB_MAPNAME (SYSCONF_DIR "/krb.equiv")
73 #endif
74 
75 struct auth_state {
76     char userid[MAX_K_NAME_SZ+1];
77     char aname[ANAME_SZ];
78     char inst[INST_SZ];
79     char realm[REALM_SZ];
80 };
81 
82 static struct auth_state auth_anonymous = {
83     "anonymous", "anonymous", "", ""
84 };
85 
86 
87 
88 static int parse_krbequiv_line (const char *src,
89                                   char *principal, char *localuser);
90 static char *auth_map_krbid (const char *real_aname, const char *real_inst,
91                              const char *real_realm);
92 
93 /*
94  * Determine if the user is a member of 'identifier'
95  * Returns one of:
96  *      0       User does not match identifier
97  *      1       identifier matches everybody
98  *      2       User is in the group that is identifier
99  *      3       User is identifer
100  */
mymemberof(const struct auth_state * auth_state,const char * identifier)101 static int mymemberof(const struct auth_state *auth_state, const char *identifier)
102 {
103     char aname[ANAME_SZ];
104     char inst[INST_SZ];
105     char realm[REALM_SZ];
106 
107     if (!auth_state) auth_state = &auth_anonymous;
108 
109     if (strcmp(identifier, "anyone") == 0) return 1;
110 
111     if (strcmp(identifier, auth_state->userid) == 0) return 3;
112 
113     /* "anonymous" is not a member of any group */
114     if (strcmp(auth_state->userid, "anonymous") == 0) return 0;
115 
116     aname[0] = inst[0] = realm[0] = '\0';
117     if (kname_parse(aname, inst, realm, (char *) identifier) != 0) {
118         return 0;
119     }
120 
121     if (strcmp(aname, auth_state->aname) != 0 && strcmp(aname, "*") != 0) {
122         return 0;
123     }
124     if (strcmp(inst, auth_state->inst) != 0 && strcmp(inst, "*") != 0) {
125         return 0;
126     }
127     if (strcmp(realm, auth_state->realm) != 0 && strcmp(realm, "*") != 0) {
128         return 0;
129     }
130     return 2;
131 }
132 
133 /*
134  * Parse a line 'src' from an /etc/krb.equiv file.
135  * Sets the buffer pointed to by 'principal' to be the kerberos
136  * identity and sets the buffer pointed to by 'localuser' to
137  * be the local user.  Both buffers must be of size one larger than
138  * MAX_K_NAME_SZ.  Returns 1 on success, 0 on failure.
139  */
140 static int
parse_krbequiv_line(const char * src,char * principal,char * localuser)141 parse_krbequiv_line(const char *src, char *principal, char *localuser)
142 {
143     int i;
144 
145     while (Uisspace(*src)) src++;
146     if (!*src) return 0;
147 
148     for (i = 0; *src && !Uisspace(*src); i++) {
149         if (i >= MAX_K_NAME_SZ) return 0;
150         *principal++ = *src++;
151     }
152     *principal = 0;
153 
154     if (!Uisspace(*src)) return 0; /* Need at least one separator */
155     while (Uisspace(*src)) src++;
156     if (!*src) return 0;
157 
158     for (i = 0; *src && !Uisspace(*src); i++) {
159         if (i >= MAX_K_NAME_SZ) return 0;
160         *localuser++ = *src++;
161     }
162     *localuser = 0;
163     return 1;
164 }
165 
166 /*
167  * Map a remote kerberos principal to a local username.  If a mapping
168  * is found, a pointer to the local username is returned.  Otherwise,
169  * a NULL pointer is returned.
170  * Eventually, this may be more sophisticated than a simple file scan.
171  */
auth_map_krbid(real_aname,real_inst,real_realm)172 static char *auth_map_krbid(real_aname, real_inst, real_realm)
173 const char *real_aname;
174 const char *real_inst;
175 const char *real_realm;
176 {
177     static char localuser[MAX_K_NAME_SZ + 1];
178     char principal[MAX_K_NAME_SZ + 1];
179     char aname[ANAME_SZ];
180     char inst[INST_SZ];
181     char realm[REALM_SZ];
182     char lrealm[REALM_SZ];
183     char krbhst[256];
184     char *p;
185     char buf[1024];
186     FILE *mapfile;
187 
188     if (!(mapfile = fopen(KRB_MAPNAME, "r"))) {
189         /* If the file can't be opened, don't do mappings */
190         return 0;
191     }
192 
193     for (;;) {
194         if (!fgets(buf, sizeof(buf), mapfile)) break;
195         if (parse_krbequiv_line(buf, principal, localuser) == 0 ||
196             kname_parse(aname, inst, realm, principal) != 0) {
197             /* Ignore badly formed lines */
198             continue;
199         }
200         if (!strcmp(aname, real_aname) && !strcmp(inst, real_inst) &&
201             !strcmp(realm, real_realm)) {
202             fclose(mapfile);
203 
204             aname[0] = inst[0] = realm[0] = '\0';
205             if (kname_parse(aname, inst, realm, localuser) != 0) {
206                 return 0;
207             }
208 
209             /* Upcase realm name */
210             for (p = realm; *p; p++) {
211                 if (Uislower(*p)) *p = toupper(*p);
212             }
213 
214             if (*realm) {
215                 if (krb_get_lrealm(lrealm,1) == 0 &&
216                     strcmp(lrealm, realm) == 0) {
217                     *realm = 0;
218                 }
219                 else if (krb_get_krbhst(krbhst, realm, 1)) {
220                     return 0;           /* Unknown realm */
221                 }
222             }
223 
224             strcpy(localuser, aname);
225             if (*inst) {
226                 strcat(localuser, ".");
227                 strcat(localuser, inst);
228             }
229             if (*realm) {
230                 strcat(localuser, "@");
231                 strcat(localuser, realm);
232             }
233 
234             return localuser;
235         }
236     }
237 
238     fclose(mapfile);
239     return 0;
240 }
241 
242 /*
243  * Convert 'identifier' into canonical form.
244  * Returns a pointer to a static buffer containing the canonical form
245  * or NULL if 'identifier' is invalid.
246  */
mycanonifyid(const char * identifier,size_t len)247 static const char *mycanonifyid(const char *identifier, size_t len)
248 {
249     static char retbuf[MAX_K_NAME_SZ+1];
250     char aname[ANAME_SZ];
251     char inst[INST_SZ];
252     char realm[REALM_SZ];
253     char lrealm[REALM_SZ];
254     char krbhst[256];
255     char *canon_buf;
256     char *p;
257 
258     if(!len) len = strlen(identifier);
259 
260     canon_buf = malloc(len + 1);
261     if(!canon_buf) return 0;
262     memcpy(canon_buf, identifier, len);
263     canon_buf[len] = '\0';
264 
265     aname[0] = inst[0] = realm[0] = '\0';
266     if (kname_parse(aname, inst, realm, canon_buf) != 0) {
267         free(canon_buf);
268         return 0;
269     }
270 
271     free(canon_buf);
272 
273     /* Upcase realm name */
274     for (p = realm; *p; p++) {
275         if (Uislower(*p)) *p = toupper(*p);
276     }
277 
278     if (*realm) {
279         if (krb_get_lrealm(lrealm,1) == 0 &&
280             strcmp(lrealm, realm) == 0) {
281             *realm = 0;
282         }
283         else if (krb_get_krbhst(krbhst, realm, 1)) {
284             return 0;           /* Unknown realm */
285         }
286     }
287 
288     /* Check for krb.equiv remappings. */
289     if ((p = auth_map_krbid(aname, inst, realm)) ) {
290         strcpy(retbuf, p);
291         return retbuf;
292     }
293 
294     strcpy(retbuf, aname);
295     if (*inst) {
296         strcat(retbuf, ".");
297         strcat(retbuf, inst);
298     }
299     if (*realm) {
300         strcat(retbuf, "@");
301         strcat(retbuf, realm);
302     }
303 
304     return retbuf;
305 }
306 
307 /*
308  * Set the current user to 'identifier'.  'cacheid', if non-null,
309  * points to a 16-byte binary key to cache identifier's information
310  * with.
311  */
mynewstate(const char * identifier)312 static struct auth_state *mynewstate(const char *identifier)
313 {
314     struct auth_state *newstate;
315 
316     identifier = auth_canonifyid(identifier, 0);
317     if (!identifier) return 0;
318 
319     newstate = (struct auth_state *)xmalloc(sizeof(struct auth_state));
320 
321     strcpy(newstate->userid, identifier);
322     newstate->aname[0] = newstate->inst[0] = newstate->realm[0] = '\0';
323     kname_parse(newstate->aname, newstate->inst, newstate->realm, (char *) identifier);
324 
325     return newstate;
326 }
327 
myfreestate(struct auth_state * auth_state)328 static void myfreestate(struct auth_state *auth_state)
329 {
330     free((char *)auth_state);
331 }
332 
make_krb_wildcard(const char * aname,const char * inst,const char * realm)333 static char *make_krb_wildcard(const char *aname, const char *inst, const char *realm)
334 {
335     return strconcat(
336         (aname ? aname : "*"),
337         ".",
338         (inst ? inst : "*"),
339         "@",
340         (realm ? realm : "*"),
341         NULL
342     );
343 }
344 
345 /* KRB4 groups are just principals with wildcarded components.
346  * XXX This hasn't even been so much as compile-tested for lack of
347  * a kerberos test environment!  If you use this, please provide
348  * feedback.
349  */
mygroups(const struct auth_state * auth_state)350 static strarray_t *mygroups(const struct auth_state *auth_state)
351 {
352     strarray_t *sa = strarray_new();
353     char *tmp = NULL;
354 
355     /* *.*@* */
356     tmp = make_krb_wildcard(NULL, NULL, NULL);
357     strarray_appendm(sa, tmp);
358 
359     /* *.*@realm */
360     if (auth_state->realm) {
361         tmp = make_krb_wildcard(NULL, NULL, auth_state->realm);
362         strarray_appendm(sa, tmp);
363     }
364 
365     /* *.inst@* */
366     if (auth_state->inst) {
367         tmp = make_krb_wildcard(NULL, auth_state->inst, NULL);
368         strarray_appendm(sa, tmp);
369         if (auth_state->realm) {
370             tmp = make_krb_wildcard(NULL, auth_state->inst, auth_state->realm);
371             strarray_appendm(sa, tmp);
372         }
373     }
374 
375     /* aname.*@* */
376     if (auth_state->aname) {
377         tmp = make_krb_wildcard(auth_state->aname, NULL, NULL);
378         strarray_appendm(sa, tmp);
379         if (auth_state->realm) {
380             tmp = make_krb_wildcard(auth_state->aname, NULL, auth_state->realm);
381             strarray_appendm(sa, tmp);
382         }
383         if (auth_state->inst) {
384             tmp = make_krb_wildcard(auth_state->aname, auth_state->inst, NULL);
385             strarray_appendm(sa, tmp);
386         }
387         /* n.b. non-wildcard "aname.inst@realm" is NOT a group! */
388     }
389 
390     return sa;
391 }
392 
393 #else /* HAVE_KRB */
394 
mymemberof(const struct auth_state * auth_state,const char * identifier)395 static int mymemberof(
396     const struct auth_state *auth_state __attribute__((unused)),
397     const char *identifier __attribute__((unused)))
398 {
399         fatal("Authentication mechanism (krb) not compiled in", EX_CONFIG);
400         return 0;
401 }
402 
mycanonifyid(const char * identifier,size_t len)403 static const char *mycanonifyid(
404     const char *identifier __attribute__((unused)),
405     size_t len __attribute__((unused)))
406 {
407         fatal("Authentication mechanism (krb) not compiled in", EX_CONFIG);
408         return NULL;
409 }
410 
mynewstate(const char * identifier)411 static struct auth_state *mynewstate(
412     const char *identifier __attribute__((unused)))
413 {
414         fatal("Authentication mechanism (krb) not compiled in", EX_CONFIG);
415         return NULL;
416 }
417 
myfreestate(struct auth_state * auth_state)418 static void myfreestate(
419     struct auth_state *auth_state __attribute__((unused)))
420 {
421         fatal("Authentication mechanism (krb) not compiled in", EX_CONFIG);
422 }
423 
mygroups(const struct auth_state * auth_state)424 static strarray_t *mygroups(
425     const struct auth_state *auth_state __attribute__((unused)))
426 {
427         fatal("Authentication mechanism (krb) not compiled in", EX_CONFIG);
428 }
429 
430 #endif
431 
432 HIDDEN struct auth_mech auth_krb =
433 {
434     "krb",              /* name */
435 
436     &mycanonifyid,
437     &mymemberof,
438     &mynewstate,
439     &myfreestate,
440     &mygroups,
441 };
442