Imported Upstream version 1.15.1
[platform/upstream/krb5.git] / src / lib / krb5 / ccache / ccselect_k5identity.c
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/ccache/ccselect_k5identity.c - k5identity ccselect module */
3 /*
4  * Copyright (C) 2011 by the Massachusetts Institute of Technology.
5  * All rights reserved.
6  *
7  * Export of this software from the United States of America may
8  *   require a specific license from the United States Government.
9  *   It is the responsibility of any person or organization contemplating
10  *   export to obtain such a license before exporting.
11  *
12  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13  * distribute this software and its documentation for any purpose and
14  * without fee is hereby granted, provided that the above copyright
15  * notice appear in all copies and that both that copyright notice and
16  * this permission notice appear in supporting documentation, and that
17  * the name of M.I.T. not be used in advertising or publicity pertaining
18  * to distribution of the software without specific, written prior
19  * permission.  Furthermore if you modify this software you must label
20  * your software as modified software and not distribute it in such a
21  * fashion that it might be confused with the original M.I.T. software.
22  * M.I.T. makes no representations about the suitability of
23  * this software for any purpose.  It is provided "as is" without express
24  * or implied warranty.
25  */
26
27 #include "k5-int.h"
28 #include "cc-int.h"
29 #include <krb5/ccselect_plugin.h>
30 #include <ctype.h>
31
32 #ifndef _WIN32
33
34 #include <pwd.h>
35
36 static krb5_error_code
37 k5identity_init(krb5_context context, krb5_ccselect_moddata *data_out,
38                 int *priority_out)
39 {
40     *data_out = NULL;
41     *priority_out = KRB5_CCSELECT_PRIORITY_AUTHORITATIVE;
42     return 0;
43 }
44
45 /* Match data (folded to lowercase if fold_case is set) against pattern. */
46 static krb5_boolean
47 fnmatch_data(const char *pattern, krb5_data *data, krb5_boolean fold_case)
48 {
49     krb5_error_code ret;
50     char *str, *p;
51     int res;
52
53     str = k5memdup0(data->data, data->length, &ret);
54     if (str == NULL)
55         return FALSE;
56
57     if (fold_case) {
58         for (p = str; *p != '\0'; p++) {
59             if (isupper((unsigned char)*p))
60                 *p = tolower((unsigned char)*p);
61         }
62     }
63
64     res = fnmatch(pattern, str, 0);
65     free(str);
66     return (res == 0);
67 }
68
69 /* Return true if server satisfies the constraint given by name and value. */
70 static krb5_boolean
71 check_constraint(krb5_context context, const char *name, const char *value,
72                  krb5_principal server)
73 {
74     if (strcmp(name, "realm") == 0) {
75         return fnmatch_data(value, &server->realm, FALSE);
76     } else if (strcmp(name, "service") == 0) {
77         return (server->type == KRB5_NT_SRV_HST && server->length >= 2 &&
78                 fnmatch_data(value, &server->data[0], FALSE));
79     } else if (strcmp(name, "host") == 0) {
80         return (server->type == KRB5_NT_SRV_HST && server->length >= 2 &&
81                 fnmatch_data(value, &server->data[1], TRUE));
82     }
83     /* Assume unrecognized constraints are critical. */
84     return FALSE;
85 }
86
87 /*
88  * If line begins with a valid principal and server matches the constraints
89  * listed afterwards, set *princ_out to the client principal described in line
90  * and return true.  Otherwise return false.  May destructively affect line.
91  */
92 static krb5_boolean
93 parse_line(krb5_context context, char *line, krb5_principal server,
94            krb5_principal *princ_out)
95 {
96     const char *whitespace = " \t\r\n";
97     char *princ, *princ_end, *field, *field_end, *sep;
98
99     *princ_out = NULL;
100
101     /* Find the bounds of the principal. */
102     princ = line + strspn(line, whitespace);
103     if (*princ == '#')
104         return FALSE;
105     princ_end = princ + strcspn(princ, whitespace);
106     if (princ_end == princ)
107         return FALSE;
108
109     /* Check all constraints. */
110     field = princ_end + strspn(princ_end, whitespace);
111     while (*field != '\0') {
112         field_end = field + strcspn(field, whitespace);
113         if (*field_end != '\0')
114             *field_end++ = '\0';
115         sep = strchr(field, '=');
116         if (sep == NULL)        /* Malformed line. */
117             return FALSE;
118         *sep = '\0';
119         if (!check_constraint(context, field, sep + 1, server))
120             return FALSE;
121         field = field_end + strspn(field_end, whitespace);
122     }
123
124     *princ_end = '\0';
125     return (krb5_parse_name(context, princ, princ_out) == 0);
126 }
127
128 /* Determine the current user's homedir.  Allow HOME to override the result for
129  * non-secure profiles; otherwise, use the euid's homedir from passwd. */
130 static char *
131 get_homedir(krb5_context context)
132 {
133     const char *homedir = NULL;
134     char pwbuf[BUFSIZ];
135     struct passwd pwx, *pwd;
136
137     if (!context->profile_secure)
138         homedir = getenv("HOME");
139
140     if (homedir == NULL) {
141         if (k5_getpwuid_r(geteuid(), &pwx, pwbuf, sizeof(pwbuf), &pwd) != 0)
142             return NULL;
143         homedir = pwd->pw_dir;
144     }
145
146     return strdup(homedir);
147 }
148
149 static krb5_error_code
150 k5identity_choose(krb5_context context, krb5_ccselect_moddata data,
151                   krb5_principal server, krb5_ccache *cache_out,
152                   krb5_principal *princ_out)
153 {
154     krb5_error_code ret;
155     krb5_principal princ = NULL;
156     char *filename, *homedir;
157     FILE *fp;
158     char buf[256];
159
160     *cache_out = NULL;
161     *princ_out = NULL;
162
163     /* Open the .k5identity file. */
164     homedir = get_homedir(context);
165     if (homedir == NULL)
166         return KRB5_PLUGIN_NO_HANDLE;
167     ret = k5_path_join(homedir, ".k5identity", &filename);
168     free(homedir);
169     if (ret)
170         return ret;
171     fp = fopen(filename, "r");
172     free(filename);
173     if (fp == NULL)
174         return KRB5_PLUGIN_NO_HANDLE;
175
176     /* Look for a line with constraints matched by server. */
177     while (fgets(buf, sizeof(buf), fp) != NULL) {
178         if (parse_line(context, buf, server, &princ))
179             break;
180     }
181     fclose(fp);
182     if (princ == NULL)
183         return KRB5_PLUGIN_NO_HANDLE;
184
185     /* Look for a ccache with the appropriate client principal.  If we don't
186      * find out, set *princ_out to indicate the desired client principal. */
187     ret = krb5_cc_cache_match(context, princ, cache_out);
188     if (ret == 0 || ret == KRB5_CC_NOTFOUND)
189         *princ_out = princ;
190     else
191         krb5_free_principal(context, princ);
192     return ret;
193 }
194
195 krb5_error_code
196 ccselect_k5identity_initvt(krb5_context context, int maj_ver, int min_ver,
197                            krb5_plugin_vtable vtable)
198 {
199     krb5_ccselect_vtable vt;
200
201     if (maj_ver != 1)
202         return KRB5_PLUGIN_VER_NOTSUPP;
203     vt = (krb5_ccselect_vtable)vtable;
204     vt->name = "k5identity";
205     vt->init = k5identity_init;
206     vt->choose = k5identity_choose;
207     return 0;
208 }
209
210 #endif /* not _WIN32 */