| File: | kauth/kkids.c |
| Location: | line 188, column 2 |
| Description: | Value stored to 'bp' is never read |
| 1 | /* |
| 2 | * Copyright 2000, International Business Machines Corporation and others. |
| 3 | * All Rights Reserved. |
| 4 | * |
| 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 |
| 8 | */ |
| 9 | |
| 10 | /* |
| 11 | * ALL RIGHTS RESERVED |
| 12 | */ |
| 13 | |
| 14 | /* These two needed for rxgen output to work */ |
| 15 | #include <afsconfig.h> |
| 16 | #include <afs/param.h> |
| 17 | #include <afs/stds.h> |
| 18 | |
| 19 | #include <roken.h> |
| 20 | |
| 21 | #ifdef AFS_NT40_ENV |
| 22 | #include <afs/fs_utils.h> |
| 23 | #else |
| 24 | #include <afs/venus.h> |
| 25 | #endif |
| 26 | |
| 27 | #include <rx/xdr.h> |
| 28 | #include <afs/prs_fs.h> |
| 29 | #include <afs/sys_prototypes.h> |
| 30 | #include <afs/afs_consts.h> |
| 31 | |
| 32 | #include "kkids.h" |
| 33 | |
| 34 | #define MAXNAME100 100 |
| 35 | |
| 36 | static int using_child = 0; |
| 37 | static FILE *childin, *childout; /* file pointers on pipe to kpwvalid */ |
| 38 | |
| 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') |
| 42 | */ |
| 43 | static int |
| 44 | simplify_name(char *orig_name, char *true_name) |
| 45 | { |
| 46 | struct stat statbuff; |
| 47 | |
| 48 | |
| 49 | #ifdef AFS_NT40_ENV |
| 50 | if (stat(orig_name, &statbuff) < 0) { |
| 51 | *true_name = '\0'; |
| 52 | return 0; |
| 53 | } else { |
| 54 | strcpy(true_name, orig_name); |
| 55 | return 1; |
| 56 | } |
| 57 | #else /* !NT40 */ |
| 58 | { |
| 59 | int link_chars_read; |
| 60 | char *last_component; |
| 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... */ |
| 64 | *true_name = '\0'; |
| 65 | return 0; |
| 66 | } |
| 67 | |
| 68 | /* |
| 69 | * The lstat succeeded. If the given file is a symlink, substitute |
| 70 | * the contents of the link for the file name. |
| 71 | */ |
| 72 | if ((statbuff.st_mode & S_IFMT0170000) == S_IFLNK0120000) { |
| 73 | link_chars_read = readlink(orig_name, true_name, 1024); |
| 74 | if (link_chars_read <= 0) { |
| 75 | *true_name = '\0'; |
| 76 | return 0; |
| 77 | } |
| 78 | |
| 79 | true_name[link_chars_read++] = '\0'; |
| 80 | |
| 81 | /* |
| 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. |
| 86 | */ |
| 87 | if (true_name[0] != '/') { |
| 88 | last_component = (char *)strrchr(orig_name, '/'); |
| 89 | strcpy(++last_component, true_name); |
| 90 | strcpy(true_name, orig_name); |
| 91 | } |
| 92 | } else |
| 93 | strcpy(true_name, orig_name); |
| 94 | |
| 95 | return 1; /* found it */ |
| 96 | } |
| 97 | #endif /* !NT40 */ |
| 98 | } |
| 99 | |
| 100 | |
| 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. |
| 110 | */ |
| 111 | static int |
| 112 | find_me(char *arg, char *parent_dir) |
| 113 | { |
| 114 | char *bp; /* basename pointer */ |
| 115 | char *dp; /* dirname pointer */ |
| 116 | char *pathelt, orig_name[1024], truename[1022]; |
| 117 | |
| 118 | #define explicitname(a,b,c)( ((a) == '/') || ( ((a) == '.') && ( ((b) == '/') || ( ((b) == '.') && ((c) == '/') ) ) ) ) \ |
| 119 | ( ((a) == '/') || \ |
| 120 | ( ((a) == '.') && \ |
| 121 | ( ((b) == '/') || \ |
| 122 | ( ((b) == '.') && ((c) == '/') ) \ |
| 123 | ) \ |
| 124 | ) \ |
| 125 | ) |
| 126 | |
| 127 | if (strlen(arg) > 510) /* just give up */ |
| 128 | return 0; |
| 129 | |
| 130 | *parent_dir = '\0'; |
| 131 | truename[0] = '\0'; |
| 132 | |
| 133 | if (explicitname(arg[0], arg[1], arg[2])( ((arg[0]) == '/') || ( ((arg[0]) == '.') && ( ((arg [1]) == '/') || ( ((arg[1]) == '.') && ((arg[2]) == '/' ) ) ) ) )) { |
| 134 | strcpy(orig_name, arg); |
| 135 | simplify_name(orig_name, truename); |
| 136 | } else { |
| 137 | bp = (char *)strrchr(arg, '/'); |
| 138 | if (bp) { |
| 139 | orig_name[0] = '.'; |
| 140 | orig_name[1] = '/'; |
| 141 | strcpy(orig_name + 2, arg); |
| 142 | simplify_name(orig_name, truename); |
| 143 | } |
| 144 | } |
| 145 | |
| 146 | if (!truename[0]) { /* didn't find it */ |
| 147 | char path[2046]; |
| 148 | |
| 149 | dp = getenv("PATH"); |
| 150 | if (!dp) |
| 151 | return 0; |
| 152 | strncpy(path, dp, 2045); |
| 153 | |
| 154 | for (pathelt = strtok(path, ":"); pathelt; |
| 155 | pathelt = strtok(NULL((void *)0), ":")) { |
| 156 | strncpy(orig_name, pathelt, 510); |
| 157 | |
| 158 | bp = orig_name + strlen(orig_name); |
| 159 | *bp = '/'; /* replace NUL with / */ |
| 160 | strncpy(bp + 1, arg, 510); |
| 161 | |
| 162 | if (simplify_name(orig_name, truename)) |
| 163 | break; |
| 164 | } |
| 165 | } |
| 166 | if (!truename[0]) /* didn't find it */ |
| 167 | return 0; /* give up */ |
| 168 | |
| 169 | /* DID FIND IT */ |
| 170 | /* |
| 171 | * Find rightmost slash, if any. |
| 172 | */ |
| 173 | bp = (char *)strrchr(truename, '/'); |
| 174 | if (bp) { |
| 175 | /* |
| 176 | * Found it. Designate everything before it as the parent directory, |
| 177 | * everything after it as the final component. |
| 178 | */ |
| 179 | strncpy(parent_dir, truename, bp - truename); |
| 180 | parent_dir[bp - truename] = 0; |
| 181 | bp++; /*Skip the slash */ |
| 182 | } else { |
| 183 | /* |
| 184 | * No slash appears in the given file name. Set parent_dir to the current |
| 185 | * directory, and the last component as the given name. |
| 186 | */ |
| 187 | strcpy(parent_dir, "."); |
| 188 | bp = truename; |
Value stored to 'bp' is never read | |
| 189 | } |
| 190 | |
| 191 | return 1; /* found it */ |
| 192 | } |
| 193 | |
| 194 | #define SkipLine(str){ while (*str !='\n') str++; str++; } { while (*str !='\n') str++; str++; } |
| 195 | |
| 196 | /* this function returns TRUE (1) if the file is in AFS, otherwise false (0) */ |
| 197 | static int |
| 198 | InAFS(char *apath) |
| 199 | { |
| 200 | struct ViceIoctl blob; |
| 201 | afs_int32 code; |
| 202 | char space[AFS_PIOCTL_MAXSIZE2048]; |
| 203 | |
| 204 | blob.in_size = 0; |
| 205 | blob.out_size = AFS_PIOCTL_MAXSIZE2048; |
| 206 | blob.out = space; |
| 207 | |
| 208 | code = pioctl(apath, VIOC_FILE_CELL_NAME((unsigned int) ((unsigned long) ((0x80000000) | (((sizeof(struct ViceIoctl)) & ((1 << 13) - 1)) << 16) | ((('V' )) << 8) | ((30))))), &blob, 1); |
| 209 | if (code) { |
| 210 | if ((errno(* __error()) == EINVAL22) || (errno(* __error()) == ENOENT2)) |
| 211 | return 0; |
| 212 | } |
| 213 | return 1; |
| 214 | } |
| 215 | |
| 216 | struct Acl { |
| 217 | int nplus; |
| 218 | int nminus; |
| 219 | struct AclEntry *pluslist; |
| 220 | struct AclEntry *minuslist; |
| 221 | }; |
| 222 | |
| 223 | struct AclEntry { |
| 224 | struct AclEntry *next; |
| 225 | char name[MAXNAME100]; |
| 226 | afs_int32 rights; |
| 227 | }; |
| 228 | |
| 229 | static struct Acl * |
| 230 | ParseAcl(char *astr) |
| 231 | { |
| 232 | int nplus, nminus, i, trights; |
| 233 | char tname[MAXNAME100]; |
| 234 | struct AclEntry *first, *last, *tl; |
| 235 | struct Acl *ta; |
| 236 | sscanf(astr, "%d", &nplus); |
| 237 | SkipLine(astr){ while (*astr !='\n') astr++; astr++; }; |
| 238 | sscanf(astr, "%d", &nminus); |
| 239 | SkipLine(astr){ while (*astr !='\n') astr++; astr++; }; |
| 240 | |
| 241 | ta = (struct Acl *)malloc(sizeof(struct Acl)); |
| 242 | ta->nplus = nplus; |
| 243 | |
| 244 | last = 0; |
| 245 | first = 0; |
| 246 | for (i = 0; i < nplus; i++) { |
| 247 | sscanf(astr, "%100s %d", tname, &trights); |
| 248 | SkipLine(astr){ while (*astr !='\n') astr++; astr++; }; |
| 249 | tl = (struct AclEntry *)malloc(sizeof(struct AclEntry)); |
| 250 | if (!first) |
| 251 | first = tl; |
| 252 | strcpy(tl->name, tname); |
| 253 | tl->rights = trights; |
| 254 | tl->next = 0; |
| 255 | if (last) |
| 256 | last->next = tl; |
| 257 | last = tl; |
| 258 | } |
| 259 | ta->pluslist = first; |
| 260 | |
| 261 | return ta; |
| 262 | } |
| 263 | |
| 264 | static char * |
| 265 | safestrtok(char *str, char *tok) |
| 266 | { |
| 267 | char *temp; |
| 268 | |
| 269 | if (str) |
| 270 | return (strtok(str, tok)); |
| 271 | |
| 272 | temp = strtok(NULL((void *)0), tok); |
| 273 | if (temp) |
| 274 | *(temp - 1) = *tok; |
| 275 | |
| 276 | return temp; |
| 277 | |
| 278 | } |
| 279 | |
| 280 | |
| 281 | /* If it exists, we do some fussing about whether or not this |
| 282 | * is a reasonably secure path - not that it makes *much* difference, since |
| 283 | * there's not much point in being more secure than the kpasswd executable. |
| 284 | */ |
| 285 | /* 1. is this directory in AFS? |
| 286 | * 2. Is every component of the pathname secure |
| 287 | * (ie, only system:administrators have w or a rights)? |
| 288 | */ |
| 289 | static int |
| 290 | is_secure(char *dir) |
| 291 | { |
| 292 | char *temp; |
| 293 | struct ViceIoctl blob; |
| 294 | struct AclEntry *te; |
| 295 | char space[2046]; |
| 296 | afs_int32 code; |
| 297 | struct Acl *ta; |
| 298 | |
| 299 | if (!InAFS(dir)) /* final component *must* be in AFS */ |
| 300 | return 0; |
| 301 | |
| 302 | #ifndef INSECURE |
| 303 | for (temp = safestrtok(dir, "/"); temp; temp = safestrtok(NULL((void *)0), "/")) { |
| 304 | /* strtok keeps sticking NUL in place of /, so we can look at |
| 305 | * ever-longer chunks of the path. |
| 306 | */ |
| 307 | if (!InAFS(dir)) |
| 308 | continue; |
| 309 | |
| 310 | blob.out_size = AFS_PIOCTL_MAXSIZE2048; |
| 311 | blob.in_size = 0; |
| 312 | blob.out = space; |
| 313 | code = pioctl(dir, VIOCGETAL((unsigned int) ((unsigned long) ((0x80000000) | (((sizeof(struct ViceIoctl)) & ((1 << 13) - 1)) << 16) | ((('V' )) << 8) | ((2))))), &blob, 1); |
| 314 | if (code) { |
| 315 | continue; |
| 316 | } |
| 317 | ta = ParseAcl(space); |
| 318 | if (ta->nplus <= 0) |
| 319 | continue; |
| 320 | |
| 321 | for (te = ta->pluslist; te; te = te->next) { |
| 322 | if (((te->rights & PRSFS_INSERT4) && (te->rights & PRSFS_DELETE16)) |
| 323 | || (te->rights & (PRSFS_WRITE2 | PRSFS_ADMINISTER64))) |
| 324 | if (strcmp(te->name, "system:administrators")) |
| 325 | return 0; /* somebody who we can't trust has got power */ |
| 326 | } |
| 327 | } |
| 328 | #endif /* INSECURE */ |
| 329 | |
| 330 | return 1; |
| 331 | } |
| 332 | |
| 333 | /* Then, once we've found our own location, we look for a program named |
| 334 | * kpwvalid. |
| 335 | */ |
| 336 | |
| 337 | /* look for a password-checking program named kpwvalid. |
| 338 | * It has to be in a secure place (same place as this executable) |
| 339 | */ |
| 340 | static int |
| 341 | kpwvalid_is(char *dir) |
| 342 | { |
| 343 | struct stat statbuff; |
| 344 | int len; |
| 345 | |
| 346 | len = (int)strlen(dir); |
| 347 | strcpy(dir + len, "/kpwvalid"); |
| 348 | |
| 349 | if (stat(dir, &statbuff) < 0) { |
| 350 | /* if lstat fails, it's possible that it's transient, but |
| 351 | * unlikely. Let's hope it isn't, and continue... */ |
| 352 | *(dir + len) = '\0'; |
| 353 | return 0; |
| 354 | } |
| 355 | |
| 356 | *(dir + len) = '\0'; |
| 357 | return 1; |
| 358 | } |
| 359 | |
| 360 | #ifdef AFS_NT40_ENV |
| 361 | /* We don't allow the use of kpwvalid executable scripts to set policy |
| 362 | * for passwd changes. |
| 363 | */ |
| 364 | int |
| 365 | init_child(char *myname) |
| 366 | { |
| 367 | |
| 368 | using_child = 0; |
| 369 | return using_child; |
| 370 | |
| 371 | } |
| 372 | #else /* !NT40 */ |
| 373 | int |
| 374 | init_child(char *myname) |
| 375 | { |
| 376 | int pipe1[2], pipe2[2]; |
| 377 | pid_t pid; |
| 378 | char dirpath[1024]; |
| 379 | char *argv[2]; |
| 380 | |
| 381 | if (!(find_me(myname, dirpath) && is_secure(dirpath) |
| 382 | && kpwvalid_is(dirpath))) { |
| 383 | using_child = 0; |
| 384 | return 0; |
| 385 | } |
| 386 | |
| 387 | /* make a couple of pipes, one for the child's stdin, and the other |
| 388 | * for the child's stdout. The parent writes to the former, and |
| 389 | * reads from the latter, the child reads from the former, and |
| 390 | * writes to the latter. |
| 391 | */ |
| 392 | pipe(pipe1); |
| 393 | pipe(pipe2); |
| 394 | |
| 395 | /* fork a child */ |
| 396 | pid = fork(); |
| 397 | if (pid == -1) { |
| 398 | using_child = 0; |
| 399 | perror("kpasswd: can't fork because "); |
| 400 | return (using_child); |
| 401 | } |
| 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 "); |
| 408 | exit(-1); |
| 409 | } |
| 410 | |
| 411 | strcat(dirpath, "/kpwvalid"); |
| 412 | argv[1] = NULL((void *)0); |
| 413 | argv[0] = dirpath; |
| 414 | execv(dirpath, argv); |
| 415 | return 0; |
| 416 | } else { |
| 417 | using_child = pid; /* save it for later */ |
| 418 | childin = fdopen(pipe1[1], "w"); |
| 419 | childout = fdopen(pipe2[0], "r"); |
| 420 | return (using_child); |
| 421 | } |
| 422 | } |
| 423 | #endif /* not NT40 */ |
| 424 | |
| 425 | int |
| 426 | password_bad(char *pw) |
| 427 | { |
| 428 | int rc; |
| 429 | rc = 0; |
| 430 | |
| 431 | if (using_child) { |
| 432 | fprintf(childin, "%s\n", pw); |
| 433 | fflush(childin); |
| 434 | fscanf(childout, "%d", &rc); |
| 435 | } |
| 436 | |
| 437 | return (rc); |
| 438 | } |
| 439 | |
| 440 | /* this is originally only used to give the child the old password, so she |
| 441 | * can compare putative new passwords against it. |
| 442 | */ |
| 443 | int |
| 444 | give_to_child(char *pw) |
| 445 | { |
| 446 | int rc; |
| 447 | rc = 0; |
| 448 | |
| 449 | if (using_child) { |
| 450 | fprintf(childin, "%s\n", pw); |
| 451 | fflush(childin); |
| 452 | } |
| 453 | |
| 454 | return (rc); |
| 455 | } |
| 456 | |
| 457 | /* quickly and painlessly |
| 458 | */ |
| 459 | int |
| 460 | terminate_child(void) |
| 461 | { |
| 462 | int rc; |
| 463 | rc = 0; |
| 464 | |
| 465 | #ifndef AFS_NT40_ENV |
| 466 | if (using_child) { |
| 467 | rc = kill(using_child, SIGKILL9); |
| 468 | } |
| 469 | #endif |
| 470 | return (rc); |
| 471 | } |