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 <afs/param.h>
16 #include <afsconfig.h>
21 #include <sys/types.h>
26 #include <afs/fs_utils.h>
28 #include <netinet/in.h>
29 #include <afs/venus.h>
34 #include <afs/prs_fs.h>
41 static int using_child = 0;
42 static FILE * childin, * childout; /* file pointers on pipe to kpwvalid */
44 /* this removes symlinks from the tail end of path names.
45 * PRECONDITION: name must be either absolute ('/something') or
46 * explictly relative to current directory ('./something')
48 static int simplify_name(
58 if (stat(orig_name, &statbuff) < 0) {
63 strcpy(true_name, orig_name);
70 if (lstat(orig_name, &statbuff) < 0) {
71 /* if lstat fails, it's possible that it's transient, but
72 unlikely. Let's hope it isn't, and continue... */
78 * The lstat succeeded. If the given file is a symlink, substitute
79 * the contents of the link for the file name.
81 if ((statbuff.st_mode & S_IFMT) == S_IFLNK) {
83 link_chars_read = readlink(orig_name, true_name, 1024);
84 if (link_chars_read <= 0) {
89 true_name[link_chars_read++] = '\0';
92 * If the symlink is an absolute pathname, we're fine. Otherwise, we
93 * have to create a full pathname using the original name and the
94 * relative symlink name. Find the rightmost slash in the original
95 * name (we know there is one) and splice in the symlink contents.
97 if (true_name[0] != '/') {
98 last_component = (char *) rindex(orig_name, '/');
99 strcpy(++last_component, true_name);
100 strcpy(true_name, orig_name);
104 strcpy(true_name, orig_name);
106 return 1 ; /* found it */
112 /* We find our own location by:
113 * 1. checking for an absolute or relative path name in argv[0]
114 * this is moderately system-dependant: argv[0] is just a convention.
115 * 2. successively checking each component of PATH, and concatenating argv[0]
116 * onto it, then stating the result.
117 * if it exists, it must be us, eh?
118 * NB there may be possible security implications involving
119 * symlinks; I think they are only relevant if the symlink points
120 * directly at kpasswd, not when it points at kpasswd's parent directory.
126 char * bp; /* basename pointer */
127 char * dp; /* dirname pointer */
128 char *pathelt, orig_name[1024], truename[1022];
130 #define explicitname(a,b,c) \
134 ( ((b) == '.') && ((c) == '/') ) \
139 if (strlen(arg) > 510) /* just give up */
145 if (explicitname(arg[0], arg[1], arg[2])) {
146 strcpy (orig_name, arg);
147 simplify_name(orig_name, truename);
150 bp = (char *) rindex(arg, '/');
154 strcpy (orig_name+2, arg);
155 simplify_name(orig_name, truename);
159 if (!truename[0]) { /* didn't find it */
165 strncpy (path, dp, 2045);
167 for (pathelt = strtok(path, ":"); pathelt; pathelt = strtok(NULL, ":")) {
168 strncpy (orig_name, pathelt, 510);
170 bp = orig_name + strlen(orig_name);
171 *bp = '/'; /* replace NUL with / */
172 strncpy (bp + 1, arg, 510);
174 if (simplify_name(orig_name, truename))
178 if (!truename[0]) /* didn't find it */
179 return 0; /* give up */
183 * Find rightmost slash, if any.
185 bp = (char *) rindex(truename, '/');
188 * Found it. Designate everything before it as the parent directory,
189 * everything after it as the final component.
191 strncpy(parent_dir, truename, bp - truename);
192 parent_dir[bp - truename] = 0;
193 bp++; /*Skip the slash*/
197 * No slash appears in the given file name. Set parent_dir to the current
198 * directory, and the last component as the given name.
200 strcpy(parent_dir, ".");
204 return 1; /* found it */
207 #define SkipLine(str) { while (*str !='\n') str++; str++; }
209 /* this function returns TRUE (1) if the file is in AFS, otherwise false (0) */
210 static int InAFS(register char *apath)
212 struct ViceIoctl blob;
213 register afs_int32 code;
217 blob.out_size = MAXSIZE;
220 code = pioctl(apath, VIOC_FILE_CELL_NAME, &blob, 1);
222 if ((errno == EINVAL) || (errno == ENOENT)) return 0;
230 struct AclEntry *pluslist;
231 struct AclEntry *minuslist;
235 struct AclEntry *next;
240 static struct Acl * ParseAcl (char *astr)
242 int nplus, nminus, i, trights;
244 struct AclEntry *first, *last, *tl;
246 sscanf(astr, "%d", &nplus);
248 sscanf(astr, "%d", &nminus);
251 ta = (struct Acl *) malloc (sizeof (struct Acl));
256 for(i=0;i<nplus;i++) {
257 sscanf(astr, "%100s %d", tname, &trights);
259 tl = (struct AclEntry *) malloc(sizeof (struct AclEntry));
260 if (!first) first = tl;
261 strcpy(tl->name, tname);
262 tl->rights = trights;
264 if (last) last->next = tl;
267 ta->pluslist = first;
272 static char *safestrtok(
278 if (str) return (strtok(str, tok));
280 temp = strtok (NULL, tok);
281 if (temp) *(temp-1) = *tok;
288 /* If it exists, we do some fussing about whether or not this
289 * is a reasonably secure path - not that it makes *much* difference, since
290 * there's not much point in being more secure than the kpasswd executable.
292 /* 1. is this directory in AFS?
293 * 2. Is every component of the pathname secure
294 * (ie, only system:administrators have w or a rights)?
296 static int is_secure (char *dir)
299 struct ViceIoctl blob;
306 if (!InAFS(dir)) /* final component *must* be in AFS */
310 for (temp = safestrtok(dir, "/"); temp; temp = safestrtok(NULL, "/")) {
311 /* strtok keeps sticking NUL in place of /, so we can look at
312 * ever-longer chunks of the path.
317 blob.out_size = MAXSIZE;
320 code = pioctl(dir, VIOCGETAL, &blob, 1);
324 ta = ParseAcl(space);
328 for(te = ta->pluslist;te;te=te->next) {
329 if (((te->rights & PRSFS_INSERT) && (te->rights & PRSFS_DELETE)) ||
330 (te->rights & (PRSFS_WRITE | PRSFS_ADMINISTER)))
331 if (strcmp (te->name, "system:administrators"))
332 return 0; /* somebody who we can't trust has got power */
335 #endif /* INSECURE */
340 /* Then, once we've found our own location, we look for a program named
344 /* look for a password-checking program named kpwvalid.
345 * It has to be in a secure place (same place as this executable)
347 static int kpwvalid_is(char *dir)
349 struct stat statbuff;
353 strcpy (dir+len, "/kpwvalid");
355 if (stat(dir, &statbuff) < 0) {
356 /* if lstat fails, it's possible that it's transient, but
357 unlikely. Let's hope it isn't, and continue... */
367 /* We don't allow the use of kpwvalid executable scripts to set policy
368 * for passwd changes.
370 int init_child(char *myname)
378 int init_child(char *myname)
380 int pipe1[2], pipe2[2];
385 if (!(find_me (myname, dirpath) && is_secure (dirpath) &&
386 kpwvalid_is(dirpath))) {
391 /* make a couple of pipes, one for the child's stdin, and the other
392 * for the child's stdout. The parent writes to the former, and
393 * reads from the latter, the child reads from the former, and
394 * writes to the latter.
403 perror("kpasswd: can't fork because ");
404 return( using_child );
406 if (pid == 0) { /* in child process */
407 /* tie stdin and stdout to these pipes */
408 /* if dup2 doesn't exist everywhere, close and then dup, but make */
409 /* sure that you really get stdin or stdout from the dup. */
410 if (( -1 == dup2(pipe1[0],0)) ||
411 ( -1 == dup2(pipe2[1],1))) {
412 perror("kpasswd: can't exec kpwvalid because ");
416 strcat (dirpath, "/kpwvalid");
419 execv(dirpath, argv);
422 using_child = pid; /* save it for later */
423 childin = fdopen(pipe1[1], "w");
424 childout = fdopen(pipe2[0], "r");
425 return (using_child);
428 #endif /* not NT40 */
430 int password_bad (char *pw)
436 fprintf (childin, "%s\n",pw);
438 fscanf (childout, "%d", &rc);
444 /* this is originally only used to give the child the old password, so she
445 * can compare putative new passwords against it.
447 int give_to_child(char *pw)
453 fprintf (childin, "%s\n",pw);
460 /* quickly and painlessly
462 int terminate_child(char *pw)
469 rc = kill(using_child, SIGKILL);