2 * Copyright 2000, International Business Machines Corporation and others.
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
14 /* These two needed for rxgen output to work */
15 #include <afsconfig.h>
16 #include <afs/param.h>
20 #include <sys/types.h>
25 #include <afs/fs_utils.h>
28 #include <netinet/in.h>
29 #include <afs/venus.h>
34 #include <afs/prs_fs.h>
37 #include <afs/sys_prototypes.h>
38 #include <afs/afs_consts.h>
44 static int using_child = 0;
45 static FILE *childin, *childout; /* file pointers on pipe to kpwvalid */
47 /* this removes symlinks from the tail end of path names.
48 * PRECONDITION: name must be either absolute ('/something') or
49 * explictly relative to current directory ('./something')
52 simplify_name(char *orig_name, char *true_name)
58 if (stat(orig_name, &statbuff) < 0) {
62 strcpy(true_name, orig_name);
69 if (lstat(orig_name, &statbuff) < 0) {
70 /* if lstat fails, it's possible that it's transient, but
71 * unlikely. Let's hope it isn't, and continue... */
77 * The lstat succeeded. If the given file is a symlink, substitute
78 * the contents of the link for the file name.
80 if ((statbuff.st_mode & S_IFMT) == S_IFLNK) {
81 link_chars_read = readlink(orig_name, true_name, 1024);
82 if (link_chars_read <= 0) {
87 true_name[link_chars_read++] = '\0';
90 * If the symlink is an absolute pathname, we're fine. Otherwise, we
91 * have to create a full pathname using the original name and the
92 * relative symlink name. Find the rightmost slash in the original
93 * name (we know there is one) and splice in the symlink contents.
95 if (true_name[0] != '/') {
96 last_component = (char *)strrchr(orig_name, '/');
97 strcpy(++last_component, true_name);
98 strcpy(true_name, orig_name);
101 strcpy(true_name, orig_name);
103 return 1; /* found it */
109 /* We find our own location by:
110 * 1. checking for an absolute or relative path name in argv[0]
111 * this is moderately system-dependant: argv[0] is just a convention.
112 * 2. successively checking each component of PATH, and concatenating argv[0]
113 * onto it, then stating the result.
114 * if it exists, it must be us, eh?
115 * NB there may be possible security implications involving
116 * symlinks; I think they are only relevant if the symlink points
117 * directly at kpasswd, not when it points at kpasswd's parent directory.
120 find_me(char *arg, char *parent_dir)
122 char *bp; /* basename pointer */
123 char *dp; /* dirname pointer */
124 char *pathelt, orig_name[1024], truename[1022];
126 #define explicitname(a,b,c) \
130 ( ((b) == '.') && ((c) == '/') ) \
135 if (strlen(arg) > 510) /* just give up */
141 if (explicitname(arg[0], arg[1], arg[2])) {
142 strcpy(orig_name, arg);
143 simplify_name(orig_name, truename);
145 bp = (char *)strrchr(arg, '/');
149 strcpy(orig_name + 2, arg);
150 simplify_name(orig_name, truename);
154 if (!truename[0]) { /* didn't find it */
160 strncpy(path, dp, 2045);
162 for (pathelt = strtok(path, ":"); pathelt;
163 pathelt = strtok(NULL, ":")) {
164 strncpy(orig_name, pathelt, 510);
166 bp = orig_name + strlen(orig_name);
167 *bp = '/'; /* replace NUL with / */
168 strncpy(bp + 1, arg, 510);
170 if (simplify_name(orig_name, truename))
174 if (!truename[0]) /* didn't find it */
175 return 0; /* give up */
179 * Find rightmost slash, if any.
181 bp = (char *)strrchr(truename, '/');
184 * Found it. Designate everything before it as the parent directory,
185 * everything after it as the final component.
187 strncpy(parent_dir, truename, bp - truename);
188 parent_dir[bp - truename] = 0;
189 bp++; /*Skip the slash */
192 * No slash appears in the given file name. Set parent_dir to the current
193 * directory, and the last component as the given name.
195 strcpy(parent_dir, ".");
199 return 1; /* found it */
202 #define SkipLine(str) { while (*str !='\n') str++; str++; }
204 /* this function returns TRUE (1) if the file is in AFS, otherwise false (0) */
208 struct ViceIoctl blob;
210 char space[AFS_PIOCTL_MAXSIZE];
213 blob.out_size = AFS_PIOCTL_MAXSIZE;
216 code = pioctl(apath, VIOC_FILE_CELL_NAME, &blob, 1);
218 if ((errno == EINVAL) || (errno == ENOENT))
227 struct AclEntry *pluslist;
228 struct AclEntry *minuslist;
232 struct AclEntry *next;
240 int nplus, nminus, i, trights;
242 struct AclEntry *first, *last, *tl;
244 sscanf(astr, "%d", &nplus);
246 sscanf(astr, "%d", &nminus);
249 ta = (struct Acl *)malloc(sizeof(struct Acl));
254 for (i = 0; i < nplus; i++) {
255 sscanf(astr, "%100s %d", tname, &trights);
257 tl = (struct AclEntry *)malloc(sizeof(struct AclEntry));
260 strcpy(tl->name, tname);
261 tl->rights = trights;
267 ta->pluslist = first;
273 safestrtok(char *str, char *tok)
278 return (strtok(str, tok));
280 temp = strtok(NULL, tok);
289 /* If it exists, we do some fussing about whether or not this
290 * is a reasonably secure path - not that it makes *much* difference, since
291 * there's not much point in being more secure than the kpasswd executable.
293 /* 1. is this directory in AFS?
294 * 2. Is every component of the pathname secure
295 * (ie, only system:administrators have w or a rights)?
301 struct ViceIoctl blob;
307 if (!InAFS(dir)) /* final component *must* be in AFS */
311 for (temp = safestrtok(dir, "/"); temp; temp = safestrtok(NULL, "/")) {
312 /* strtok keeps sticking NUL in place of /, so we can look at
313 * ever-longer chunks of the path.
318 blob.out_size = AFS_PIOCTL_MAXSIZE;
321 code = pioctl(dir, VIOCGETAL, &blob, 1);
325 ta = ParseAcl(space);
329 for (te = ta->pluslist; te; te = te->next) {
330 if (((te->rights & PRSFS_INSERT) && (te->rights & PRSFS_DELETE))
331 || (te->rights & (PRSFS_WRITE | PRSFS_ADMINISTER)))
332 if (strcmp(te->name, "system:administrators"))
333 return 0; /* somebody who we can't trust has got power */
336 #endif /* INSECURE */
341 /* Then, once we've found our own location, we look for a program named
345 /* look for a password-checking program named kpwvalid.
346 * It has to be in a secure place (same place as this executable)
349 kpwvalid_is(char *dir)
351 struct stat statbuff;
354 len = (int)strlen(dir);
355 strcpy(dir + len, "/kpwvalid");
357 if (stat(dir, &statbuff) < 0) {
358 /* if lstat fails, it's possible that it's transient, but
359 * unlikely. Let's hope it isn't, and continue... */
369 /* We don't allow the use of kpwvalid executable scripts to set policy
370 * for passwd changes.
373 init_child(char *myname)
382 init_child(char *myname)
384 int pipe1[2], pipe2[2];
389 if (!(find_me(myname, dirpath) && is_secure(dirpath)
390 && kpwvalid_is(dirpath))) {
395 /* make a couple of pipes, one for the child's stdin, and the other
396 * for the child's stdout. The parent writes to the former, and
397 * reads from the latter, the child reads from the former, and
398 * writes to the latter.
407 perror("kpasswd: can't fork because ");
408 return (using_child);
410 if (pid == 0) { /* in child process */
411 /* tie stdin and stdout to these pipes */
412 /* if dup2 doesn't exist everywhere, close and then dup, but make */
413 /* sure that you really get stdin or stdout from the dup. */
414 if ((-1 == dup2(pipe1[0], 0)) || (-1 == dup2(pipe2[1], 1))) {
415 perror("kpasswd: can't exec kpwvalid because ");
419 strcat(dirpath, "/kpwvalid");
422 execv(dirpath, argv);
425 using_child = pid; /* save it for later */
426 childin = fdopen(pipe1[1], "w");
427 childout = fdopen(pipe2[0], "r");
428 return (using_child);
431 #endif /* not NT40 */
434 password_bad(char *pw)
440 fprintf(childin, "%s\n", pw);
442 fscanf(childout, "%d", &rc);
448 /* this is originally only used to give the child the old password, so she
449 * can compare putative new passwords against it.
452 give_to_child(char *pw)
458 fprintf(childin, "%s\n", pw);
465 /* quickly and painlessly
468 terminate_child(void)
475 rc = kill(using_child, SIGKILL);