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>
22 #include <afs/fs_utils.h>
24 #include <afs/venus.h>
28 #include <afs/prs_fs.h>
29 #include <afs/sys_prototypes.h>
30 #include <afs/afs_consts.h>
36 static int using_child
= 0;
37 static FILE *childin
, *childout
; /* file pointers on pipe to kpwvalid */
39 /* this removes symlinks from the tail end of path names.
40 * PRECONDITION: name must be either absolute ('/something') or
41 * explictly relative to current directory ('./something')
44 simplify_name(char *orig_name
, char *true_name
)
50 if (stat(orig_name
, &statbuff
) < 0) {
54 strcpy(true_name
, orig_name
);
61 if (lstat(orig_name
, &statbuff
) < 0) {
62 /* if lstat fails, it's possible that it's transient, but
63 * unlikely. Let's hope it isn't, and continue... */
69 * The lstat succeeded. If the given file is a symlink, substitute
70 * the contents of the link for the file name.
72 if ((statbuff
.st_mode
& S_IFMT
) == S_IFLNK
) {
73 link_chars_read
= readlink(orig_name
, true_name
, 1024);
74 if (link_chars_read
<= 0) {
79 true_name
[link_chars_read
++] = '\0';
82 * If the symlink is an absolute pathname, we're fine. Otherwise, we
83 * have to create a full pathname using the original name and the
84 * relative symlink name. Find the rightmost slash in the original
85 * name (we know there is one) and splice in the symlink contents.
87 if (true_name
[0] != '/') {
88 last_component
= (char *)strrchr(orig_name
, '/');
89 strcpy(++last_component
, true_name
);
90 strcpy(true_name
, orig_name
);
93 strcpy(true_name
, orig_name
);
95 return 1; /* found it */
101 /* We find our own location by:
102 * 1. checking for an absolute or relative path name in argv[0]
103 * this is moderately system-dependant: argv[0] is just a convention.
104 * 2. successively checking each component of PATH, and concatenating argv[0]
105 * onto it, then stating the result.
106 * if it exists, it must be us, eh?
107 * NB there may be possible security implications involving
108 * symlinks; I think they are only relevant if the symlink points
109 * directly at kpasswd, not when it points at kpasswd's parent directory.
112 find_me(char *arg
, char *parent_dir
)
114 char *bp
; /* basename pointer */
115 char *dp
; /* dirname pointer */
116 char *pathelt
, orig_name
[1024], truename
[1022];
118 #define explicitname(a,b,c) \
122 ( ((b) == '.') && ((c) == '/') ) \
127 if (strlen(arg
) > 510) /* just give up */
133 if (explicitname(arg
[0], arg
[1], arg
[2])) {
134 strcpy(orig_name
, arg
);
135 simplify_name(orig_name
, truename
);
137 bp
= (char *)strrchr(arg
, '/');
141 strcpy(orig_name
+ 2, arg
);
142 simplify_name(orig_name
, truename
);
146 if (!truename
[0]) { /* didn't find it */
152 strncpy(path
, dp
, 2045);
154 for (pathelt
= strtok(path
, ":"); pathelt
;
155 pathelt
= strtok(NULL
, ":")) {
156 strncpy(orig_name
, pathelt
, 510);
158 bp
= orig_name
+ strlen(orig_name
);
159 *bp
= '/'; /* replace NUL with / */
160 strncpy(bp
+ 1, arg
, 510);
162 if (simplify_name(orig_name
, truename
))
166 if (!truename
[0]) /* didn't find it */
167 return 0; /* give up */
171 * Find rightmost slash, if any.
173 bp
= (char *)strrchr(truename
, '/');
176 * Found it. Designate everything before it as the parent directory,
177 * everything after it as the final component.
179 strncpy(parent_dir
, truename
, bp
- truename
);
180 parent_dir
[bp
- truename
] = 0;
183 * No slash appears in the given file name. Set parent_dir to the current
184 * directory, and the last component as the given name.
186 strcpy(parent_dir
, ".");
189 return 1; /* found it */
192 #define SkipLine(str) { while (*str !='\n') str++; str++; }
194 /* this function returns TRUE (1) if the file is in AFS, otherwise false (0) */
198 struct ViceIoctl blob
;
200 char space
[AFS_PIOCTL_MAXSIZE
];
203 blob
.out_size
= AFS_PIOCTL_MAXSIZE
;
206 code
= pioctl(apath
, VIOC_FILE_CELL_NAME
, &blob
, 1);
208 if ((errno
== EINVAL
) || (errno
== ENOENT
))
217 struct AclEntry
*pluslist
;
218 struct AclEntry
*minuslist
;
222 struct AclEntry
*next
;
230 int nplus
, nminus
, i
, trights
;
232 struct AclEntry
*first
, *last
, *tl
;
234 sscanf(astr
, "%d", &nplus
);
236 sscanf(astr
, "%d", &nminus
);
239 ta
= malloc(sizeof(struct Acl
));
244 for (i
= 0; i
< nplus
; i
++) {
245 sscanf(astr
, "%100s %d", tname
, &trights
);
247 tl
= malloc(sizeof(struct AclEntry
));
250 strcpy(tl
->name
, tname
);
251 tl
->rights
= trights
;
257 ta
->pluslist
= first
;
263 safestrtok(char *str
, char *tok
)
268 return (strtok(str
, tok
));
270 temp
= strtok(NULL
, tok
);
279 /* If it exists, we do some fussing about whether or not this
280 * is a reasonably secure path - not that it makes *much* difference, since
281 * there's not much point in being more secure than the kpasswd executable.
283 /* 1. is this directory in AFS?
284 * 2. Is every component of the pathname secure
285 * (ie, only system:administrators have w or a rights)?
291 struct ViceIoctl blob
;
297 if (!InAFS(dir
)) /* final component *must* be in AFS */
301 for (temp
= safestrtok(dir
, "/"); temp
; temp
= safestrtok(NULL
, "/")) {
302 /* strtok keeps sticking NUL in place of /, so we can look at
303 * ever-longer chunks of the path.
308 blob
.out_size
= AFS_PIOCTL_MAXSIZE
;
311 code
= pioctl(dir
, VIOCGETAL
, &blob
, 1);
315 ta
= ParseAcl(space
);
319 for (te
= ta
->pluslist
; te
; te
= te
->next
) {
320 if (((te
->rights
& PRSFS_INSERT
) && (te
->rights
& PRSFS_DELETE
))
321 || (te
->rights
& (PRSFS_WRITE
| PRSFS_ADMINISTER
)))
322 if (strcmp(te
->name
, "system:administrators"))
323 return 0; /* somebody who we can't trust has got power */
326 #endif /* INSECURE */
331 /* Then, once we've found our own location, we look for a program named
335 /* look for a password-checking program named kpwvalid.
336 * It has to be in a secure place (same place as this executable)
339 kpwvalid_is(char *dir
)
341 struct stat statbuff
;
344 len
= (int)strlen(dir
);
345 strcpy(dir
+ len
, "/kpwvalid");
347 if (stat(dir
, &statbuff
) < 0) {
348 /* if lstat fails, it's possible that it's transient, but
349 * unlikely. Let's hope it isn't, and continue... */
359 /* We don't allow the use of kpwvalid executable scripts to set policy
360 * for passwd changes.
363 init_child(char *myname
)
372 init_child(char *myname
)
374 int pipe1
[2], pipe2
[2];
379 if (!(find_me(myname
, dirpath
) && is_secure(dirpath
)
380 && kpwvalid_is(dirpath
))) {
385 /* make a couple of pipes, one for the child's stdin, and the other
386 * for the child's stdout. The parent writes to the former, and
387 * reads from the latter, the child reads from the former, and
388 * writes to the latter.
390 if (pipe(pipe1
) || pipe(pipe2
)) {
399 perror("kpasswd: can't fork because ");
400 return (using_child
);
402 if (pid
== 0) { /* in child process */
403 /* tie stdin and stdout to these pipes */
404 /* if dup2 doesn't exist everywhere, close and then dup, but make */
405 /* sure that you really get stdin or stdout from the dup. */
406 if ((-1 == dup2(pipe1
[0], 0)) || (-1 == dup2(pipe2
[1], 1))) {
407 perror("kpasswd: can't exec kpwvalid because ");
411 strcat(dirpath
, "/kpwvalid");
414 execv(dirpath
, argv
);
417 using_child
= pid
; /* save it for later */
418 childin
= fdopen(pipe1
[1], "w");
419 childout
= fdopen(pipe2
[0], "r");
420 return (using_child
);
423 #endif /* not NT40 */
426 password_bad(char *pw
)
432 fprintf(childin
, "%s\n", pw
);
434 if (fscanf(childout
, "%d", &rc
) < 1)
441 /* this is originally only used to give the child the old password, so she
442 * can compare putative new passwords against it.
445 give_to_child(char *pw
)
451 fprintf(childin
, "%s\n", pw
);
458 /* quickly and painlessly
461 terminate_child(void)
468 rc
= kill(using_child
, SIGKILL
);