1 /*
2 * Copyright 2003,2004,2005,2006,2007,2008,2012,2013 Red Hat, Inc.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, and the entire permission notice in its entirety,
9 * including the disclaimer of warranties.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. The name of the author may not be used to endorse or promote
14 * products derived from this software without specific prior
15 * written permission.
16 *
17 * ALTERNATIVELY, this product may be distributed under the terms of the
18 * GNU Lesser General Public License, in which case the provisions of the
19 * LGPL are required INSTEAD OF the above restrictions.
20 *
21 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
22 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
23 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
24 * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
27 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
28 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 #include "../config.h"
34
35 #include <sys/stat.h>
36 #include <sys/types.h>
37 #include <limits.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <unistd.h>
42
43 #ifdef HAVE_SECURITY_PAM_APPL_H
44 #include <security/pam_appl.h>
45 #endif
46
47 #ifdef HAVE_SECURITY_PAM_MODULES_H
48 #include <security/pam_modules.h>
49 #endif
50
51 #include KRB5_H
52
53 #include "log.h"
54 #include "minikafs.h"
55 #include "options.h"
56 #include "stash.h"
57 #include "tokens.h"
58 #include "userinfo.h"
59 #include "v5.h"
60 #include "xstr.h"
61
62 int
tokens_useful(void)63 tokens_useful(void)
64 {
65 if (minikafs_has_afs()) {
66 return 1;
67 }
68 return 0;
69 }
70
71 static int
cell_is_in_option_list(struct _pam_krb5_options * options,const char * cell)72 cell_is_in_option_list(struct _pam_krb5_options *options, const char *cell)
73 {
74 int i;
75 for (i = 0; i < options->n_afs_cells; i++) {
76 if (strcmp(cell, options->afs_cells[i].cell) == 0) {
77 return 1;
78 }
79 }
80 return 0;
81 }
82
83 int
tokens_obtain(krb5_context context,struct _pam_krb5_stash * stash,struct _pam_krb5_options * options,struct _pam_krb5_user_info * info,int newpag)84 tokens_obtain(krb5_context context,
85 struct _pam_krb5_stash *stash,
86 struct _pam_krb5_options *options,
87 struct _pam_krb5_user_info *info, int newpag)
88 {
89 int i, ret;
90 unsigned int n;
91 char localcell[LINE_MAX], homecell[LINE_MAX], homedir[LINE_MAX],
92 lnk[LINE_MAX];
93 struct stat st;
94 uid_t uid;
95 const struct {
96 const char *name; int method;
97 } method_names[] = {
98 {"2b", MINIKAFS_METHOD_V5_2B},
99 {"rxk5", MINIKAFS_METHOD_RXK5}
100 };
101 int *methods, n_methods;
102 const char *p, *q;
103
104 if (options->debug) {
105 debug("obtaining afs tokens");
106 }
107 uid = options->user_check ? info->uid : getuid();
108
109 /* Check if AFS is running. If it isn't, no other calls to minikafs
110 * will work, or even be safe to call. */
111 if (!minikafs_has_afs()) {
112 if (stat("/afs", &st) == 0) {
113 warn("afs not running");
114 } else {
115 if (options->debug) {
116 debug("afs not running");
117 }
118 }
119 return PAM_SUCCESS;
120 }
121
122 /* Create a PAG. */
123 if (newpag) {
124 if (options->debug) {
125 debug("creating new PAG");
126 }
127 minikafs_setpag();
128 stash->afspag = 1;
129 }
130
131 /* Parse the token_strategy option. */
132 methods = malloc((strlen(options->token_strategy) + 1) * sizeof(int));
133 if (methods == NULL) {
134 return PAM_BUF_ERR;
135 }
136 memset(methods, 0, (strlen(options->token_strategy) + 1) * sizeof(int));
137 n_methods = 0;
138 p = options->token_strategy;
139 while (strlen(p) > 0) {
140 q = p + strcspn(p, ",");
141 for (n = 0;
142 n < sizeof(method_names) / sizeof(method_names[0]);
143 n++) {
144 if (strncmp(p, method_names[n].name,
145 strlen(method_names[n].name)) == 0) {
146 methods[n_methods++] = method_names[n].method;
147 }
148 }
149 p = q + strspn(q, ",");
150 }
151
152 /* Get the name of the local cell. The root.afs volume which is
153 * mounted in /afs is mounted from the local cell, so we'll use that
154 * to determine which cell is considered the local cell. Avoid getting
155 * tripped up by dynamic root support in clients. */
156 memset(localcell, '\0', sizeof(localcell));
157 if ((minikafs_ws_cell(localcell, sizeof(localcell) - 1) == 0) &&
158 (strcmp(localcell, "dynroot") != 0) &&
159 (!cell_is_in_option_list(options, localcell))) {
160 if (options->debug) {
161 debug("obtaining tokens for local cell '%s'",
162 localcell);
163 }
164 ret = minikafs_log(context, stash->v5ccache, options,
165 localcell, NULL, uid,
166 methods, n_methods);
167 if (ret != 0) {
168 if (stash->v5attempted != 0) {
169 warn("got error %d (%s) while obtaining "
170 "tokens for %s",
171 ret, v5_error_message(ret), localcell);
172 } else {
173 if (options->debug) {
174 debug("got error %d (%s) while "
175 "obtaining tokens for %s",
176 ret, v5_error_message(ret),
177 localcell);
178 }
179 }
180 }
181 }
182 /* Get the name of the cell which houses the user's home directory. In
183 * case intervening directories aren't readable by system:anyuser
184 * (which gives us an error), keep walking the directory chain until we
185 * either succeed or run out of path components to remove. And try to
186 * avoid doing the same thing twice. */
187 strncpy(homedir, info->homedir ? info->homedir : "/afs",
188 sizeof(homedir) - 1);
189 homedir[sizeof(homedir) - 1] = '\0';
190 /* A common configuration is to have the home directory be a symlink
191 * into /afs. If the homedir is a symlink, chase it, *once*. */
192 if (lstat(homedir, &st) == 0) {
193 if (st.st_mode & S_IFLNK) {
194 /* Read the link. */
195 memset(lnk, '\0', sizeof(lnk));
196 if (readlink(homedir, lnk, sizeof(lnk) - 1) == 0) {
197 /* If it's an absolute link, then we should
198 * ask about the link destination instead. */
199 if ((strlen(lnk) > 0) && (lnk[0] == '/')) {
200 strcpy(homedir, lnk);
201 }
202 }
203 }
204 }
205 i = minikafs_cell_of_file_walk_up(homedir, homecell,
206 sizeof(homecell) - 1);
207 if ((i == 0) &&
208 (strcmp(homecell, "dynroot") != 0) &&
209 (strcmp(homecell, localcell) != 0) &&
210 (!cell_is_in_option_list(options, homecell))) {
211 if (options->debug) {
212 debug("obtaining tokens for home cell '%s'", homecell);
213 }
214 ret = minikafs_log(context, stash->v5ccache, options,
215 homecell, NULL, uid,
216 methods, n_methods);
217 if (ret != 0) {
218 if (stash->v5attempted != 0) {
219 warn("got error %d (%s) while obtaining "
220 "tokens for %s",
221 ret, v5_error_message(ret), homecell);
222 } else {
223 if (options->debug) {
224 debug("got error %d (%s) while "
225 "obtaining tokens for %s",
226 ret, v5_error_message(ret),
227 homecell);
228 }
229 }
230 }
231 }
232
233 /* If there are no additional cells configured, stop here. */
234 if (options->afs_cells == NULL) {
235 if (options->debug) {
236 debug("no additional afs cells configured");
237 }
238 return PAM_SUCCESS;
239 }
240
241 /* Iterate through the list of other cells. */
242 for (i = 0; i < options->n_afs_cells; i++) {
243 if (options->debug) {
244 if (options->afs_cells[i].principal_name != NULL) {
245 debug("obtaining tokens for '%s' ('%s')",
246 options->afs_cells[i].cell,
247 options->afs_cells[i].principal_name);
248 } else {
249 debug("obtaining tokens for '%s'",
250 options->afs_cells[i].cell);
251 }
252 }
253 ret = minikafs_log(context, stash->v5ccache, options,
254 options->afs_cells[i].cell,
255 options->afs_cells[i].principal_name, uid,
256 methods, n_methods);
257 if (ret != 0) {
258 if (stash->v5attempted != 0) {
259 warn("got error %d (%s) while obtaining "
260 "tokens for %s",
261 ret, v5_error_message(ret),
262 options->afs_cells[i].cell);
263 } else {
264 if (options->debug) {
265 debug("got error %d (%s) while "
266 "obtaining tokens for %s",
267 ret, v5_error_message(ret),
268 options->afs_cells[i].cell);
269 }
270 }
271 }
272 }
273
274 /* Suppress all errors. */
275 return PAM_SUCCESS;
276 }
277
278 int
tokens_release(struct _pam_krb5_stash * stash,struct _pam_krb5_options * options)279 tokens_release(struct _pam_krb5_stash *stash, struct _pam_krb5_options *options)
280 {
281 struct stat st;
282
283 /* Check if AFS is running. If it isn't, no other calls to libkrbafs
284 * will work, or even be safe to call. */
285 if (!minikafs_has_afs()) {
286 if (stat("/afs", &st) == 0) {
287 warn("afs not running");
288 } else {
289 if (options->debug) {
290 debug("afs not running");
291 }
292 }
293 return PAM_SUCCESS;
294 }
295
296 /* Destroy all tokens. */
297 if (stash->afspag != 0) {
298 if (options->debug) {
299 debug("releasing afs tokens");
300 }
301 minikafs_unlog();
302 stash->afspag = 0;
303 }
304
305 /* Suppress all errors. */
306 return PAM_SUCCESS;
307 }
308