1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <stdio.h> 30 #include <errno.h> 31 #include <stdlib.h> 32 #include <string.h> 33 #include <sys/errno.h> 34 #include <pwd.h> 35 #include <unistd.h> 36 #include <syslog.h> 37 38 #include <netdb.h> 39 40 #include <rpc/rpc.h> 41 #include <rpcsvc/yppasswd.h> 42 #include <rpcsvc/ypclnt.h> 43 #include <rpcsvc/yp_prot.h> 44 45 #include "passwdutil.h" 46 47 int nis_getattr(char *name, attrlist *item, pwu_repository_t *rep); 48 int nis_getpwnam(char *name, attrlist *items, pwu_repository_t *rep, 49 void **buf); 50 int nis_update(attrlist *items, pwu_repository_t *rep, void *buf); 51 int nis_putpwnam(char *name, char *oldpw, char *dummy, 52 pwu_repository_t *rep, void *buf); 53 int nis_user_to_authenticate(char *user, pwu_repository_t *rep, 54 char **auth_user, int *privileged); 55 56 /* 57 * nis function pointer table, used by passwdutil_init to initialize 58 * the global Repository-OPerations table "rops" 59 */ 60 struct repops nis_repops = { 61 NULL, /* checkhistory */ 62 nis_getattr, 63 nis_getpwnam, 64 nis_update, 65 nis_putpwnam, 66 nis_user_to_authenticate, 67 NULL, /* lock */ 68 NULL /* unlock */ 69 }; 70 71 /* 72 * structure used to keep state between get/update/put calls 73 */ 74 typedef struct { 75 char *domain; 76 char *master; 77 char *scratch; 78 int scratchlen; 79 char *c2scratch; 80 int c2scratchlen; 81 struct passwd *pwd; 82 } nisbuf_t; 83 84 /* 85 * Are we a 'privileged' process? Yes if we are running on the 86 * NIS server AND we are root... 87 */ 88 int 89 nis_privileged(nisbuf_t *nisbuf) 90 { 91 char thishost[MAXHOSTNAMELEN]; 92 if (gethostname(thishost, sizeof (thishost)) == -1) { 93 syslog(LOG_ERR, "passwdutil.so: Can't get hostname"); 94 return (0); 95 } 96 97 if (strcmp(nisbuf->master, thishost) != 0) 98 return (0); 99 100 /* We're running on the NIS server. */ 101 return (getuid() == 0); 102 } 103 104 /* 105 * nis_to_pwd() 106 * 107 * convert password-entry-line to "struct passwd" 108 */ 109 void 110 nis_to_pwd(char *nis, struct passwd *pwd) 111 { 112 pwd->pw_name = strsep(&nis, ":"); 113 pwd->pw_passwd = strsep(&nis, ":"); 114 pwd->pw_uid = atoi(strsep(&nis, ":")); 115 pwd->pw_gid = atoi(strsep(&nis, ":")); 116 pwd->pw_gecos = strsep(&nis, ":"); 117 pwd->pw_dir = strsep(&nis, ":"); 118 pwd->pw_shell = nis; 119 if (pwd->pw_shell[0]) 120 pwd->pw_shell[strlen(pwd->pw_shell)-1] = '\0'; 121 } 122 123 /* 124 * nis_user_to_authenticate(name, rep, auth_user, privileged) 125 * 126 */ 127 /*ARGSUSED*/ 128 int 129 nis_user_to_authenticate(char *user, pwu_repository_t *rep, 130 char **auth_user, int *privileged) 131 { 132 nisbuf_t *buf = NULL; 133 int res; 134 attrlist attr_tmp[1]; 135 uid_t uid; 136 137 /* 138 * special NIS case: don't bother to get "root" from NIS 139 */ 140 if (strcmp(user, "root") == 0) 141 return (PWU_NOT_FOUND); 142 143 attr_tmp[0].type = ATTR_UID; 144 attr_tmp[0].next = NULL; 145 146 res = nis_getpwnam(user, &attr_tmp[0], rep, (void **)&buf); 147 148 if (res != PWU_SUCCESS) 149 return (res); 150 151 if (nis_privileged(buf)) { 152 *privileged = 1; 153 *auth_user = NULL; 154 res = PWU_SUCCESS; 155 } else { 156 uid = getuid(); 157 158 *privileged = (uid == (uid_t)0); 159 160 /* root, or user herself can change attributes */ 161 if (uid == 0 || uid == buf->pwd->pw_uid) { 162 *auth_user = strdup(user); 163 res = PWU_SUCCESS; 164 } else { 165 res = PWU_DENIED; 166 } 167 } 168 169 /* 170 * Do not release buf->domain. 171 * It's been set by yp_get_default_domain() 172 * and must not be freed. 173 * See man page yp_get_default_domain(3NSL) 174 * for details. 175 */ 176 if (buf->master) 177 free(buf->master); 178 if (buf->scratch) 179 free(buf->scratch); 180 if (buf->c2scratch) 181 free(buf->c2scratch); 182 free(buf->pwd); 183 free(buf); 184 185 return (res); 186 } 187 188 189 /* 190 * nis_getattr(name, items, rep) 191 * 192 * get account attributes specified in 'items' 193 */ 194 int 195 nis_getattr(char *name, attrlist *items, pwu_repository_t *rep) 196 { 197 nisbuf_t *nisbuf = NULL; 198 struct passwd *pw; 199 attrlist *w; 200 int res; 201 202 res = nis_getpwnam(name, items, rep, (void **)&nisbuf); 203 if (res != PWU_SUCCESS) 204 return (res); 205 206 pw = nisbuf->pwd; 207 208 for (w = items; w != NULL; w = w->next) { 209 switch (w->type) { 210 case ATTR_NAME: 211 if ((w->data.val_s = strdup(pw->pw_name)) == NULL) 212 res = PWU_NOMEM; 213 break; 214 case ATTR_COMMENT: 215 if ((w->data.val_s = strdup(pw->pw_comment)) == NULL) 216 res = PWU_NOMEM; 217 break; 218 case ATTR_GECOS: 219 if ((w->data.val_s = strdup(pw->pw_gecos)) == NULL) 220 res = PWU_NOMEM; 221 break; 222 case ATTR_HOMEDIR: 223 if ((w->data.val_s = strdup(pw->pw_dir)) == NULL) 224 res = PWU_NOMEM; 225 break; 226 case ATTR_SHELL: 227 if ((w->data.val_s = strdup(pw->pw_shell)) == NULL) 228 res = PWU_NOMEM; 229 break; 230 case ATTR_PASSWD: 231 case ATTR_PASSWD_SERVER_POLICY: 232 if ((w->data.val_s = strdup(pw->pw_passwd)) == NULL) 233 res = PWU_NOMEM; 234 break; 235 case ATTR_REP_NAME: 236 if ((w->data.val_s = strdup("nis")) == NULL) 237 res = PWU_NOMEM; 238 break; 239 240 /* integer values */ 241 case ATTR_UID: 242 w->data.val_i = nisbuf->pwd->pw_uid; 243 break; 244 case ATTR_GID: 245 w->data.val_i = nisbuf->pwd->pw_gid; 246 break; 247 case ATTR_LSTCHG: 248 case ATTR_MIN: 249 case ATTR_MAX: 250 case ATTR_WARN: 251 case ATTR_INACT: 252 case ATTR_EXPIRE: 253 case ATTR_FLAG: 254 case ATTR_AGE: 255 w->data.val_i = -1; /* not used for NIS */ 256 break; 257 default: 258 break; 259 } 260 } 261 262 /* 263 * Do not release nisbuf->domain. 264 * It's been set by yp_get_default_domain() 265 * and must not be freed. 266 * See man page yp_get_default_domain(3NSL) 267 * for details. 268 */ 269 if (nisbuf->master) 270 free(nisbuf->master); 271 if (nisbuf->scratch) 272 free(nisbuf->scratch); 273 if (nisbuf->c2scratch) 274 free(nisbuf->c2scratch); 275 free(nisbuf->pwd); 276 free(nisbuf); 277 278 return (res); 279 } 280 281 /* 282 * nis_getpwnam(name, items, rep) 283 * 284 * Get the account information of user 'name' 285 */ 286 /*ARGSUSED*/ 287 int 288 nis_getpwnam(char *name, attrlist *items, pwu_repository_t *rep, 289 void **buf) 290 { 291 nisbuf_t *nisbuf; 292 int nisresult; 293 294 nisbuf = calloc(sizeof (*nisbuf), 1); 295 if (nisbuf == NULL) 296 return (PWU_NOMEM); 297 298 nisbuf->pwd = malloc(sizeof (struct passwd)); 299 if (nisbuf->pwd == NULL) { 300 free(nisbuf); 301 return (PWU_NOMEM); 302 } 303 304 /* 305 * Do not release nisbuf->domain. 306 * It is going to be set by yp_get_default_domain() 307 * and must not be freed. 308 * See man page yp_get_default_domain(3NSL) 309 * for details. 310 */ 311 if (yp_get_default_domain(&nisbuf->domain) != 0) { 312 syslog(LOG_ERR, "passwdutil.so: can't get domain"); 313 free(nisbuf->pwd); 314 free(nisbuf); 315 return (PWU_SERVER_ERROR); 316 } 317 318 if (yp_master(nisbuf->domain, "passwd.byname", &nisbuf->master) != 0) { 319 syslog(LOG_ERR, 320 "passwdutil.so: can't get master for passwd map"); 321 if (nisbuf->master) 322 free(nisbuf->master); 323 free(nisbuf->pwd); 324 free(nisbuf); 325 return (PWU_SERVER_ERROR); 326 } 327 328 nisresult = yp_match(nisbuf->domain, "passwd.byname", name, 329 strlen(name), &(nisbuf->scratch), 330 &(nisbuf->scratchlen)); 331 if (nisresult != 0) { 332 (void) free(nisbuf->pwd); 333 if (nisbuf->scratch) 334 (void) free(nisbuf->scratch); 335 if (nisbuf->master) 336 (void) free(nisbuf->master); 337 (void) free(nisbuf); 338 return (PWU_NOT_FOUND); 339 } 340 341 nis_to_pwd(nisbuf->scratch, nisbuf->pwd); 342 343 /* 344 * check for the C2 security flag "##" in the passwd field. 345 * If the first 2 chars in the passwd field is "##", get 346 * the user's passwd from passwd.adjunct.byname map. 347 * The lookup to this passwd.adjunct.byname map will only 348 * succeed if the caller's uid is 0 because only root user 349 * can use privilege port. 350 */ 351 if (nisbuf->pwd->pw_passwd[0] == '#' && 352 nisbuf->pwd->pw_passwd[1] == '#') { 353 char *key = &nisbuf->pwd->pw_passwd[2]; 354 int keylen; 355 char *p; 356 357 keylen = strlen(key); 358 359 nisresult = yp_match(nisbuf->domain, "passwd.adjunct.byname", 360 key, keylen, &(nisbuf->c2scratch), 361 &(nisbuf->c2scratchlen)); 362 363 if (nisresult == 0 && nisbuf->c2scratch != NULL) { 364 /* Skip username (first field), and pick up password */ 365 p = nisbuf->c2scratch; 366 (void) strsep(&p, ":"); 367 nisbuf->pwd->pw_passwd = strsep(&p, ":"); 368 } 369 } 370 371 *buf = (void *)nisbuf; 372 373 return (PWU_SUCCESS); 374 } 375 376 /* 377 * nis_update(items, rep, buf) 378 * 379 * update the information in "buf" with the attribute/values 380 * specified in "items". 381 */ 382 /*ARGSUSED*/ 383 int 384 nis_update(attrlist *items, pwu_repository_t *rep, void *buf) 385 { 386 attrlist *p; 387 nisbuf_t *nisbuf = (nisbuf_t *)buf; 388 char *salt; 389 390 for (p = items; p != NULL; p = p->next) { 391 switch (p->type) { 392 case ATTR_NAME: 393 break; 394 /* 395 * Nothing special needs to be done for 396 * server policy 397 */ 398 case ATTR_PASSWD: 399 case ATTR_PASSWD_SERVER_POLICY: 400 salt = crypt_gensalt( 401 nisbuf->pwd->pw_passwd, nisbuf->pwd); 402 403 if (salt == NULL) { 404 if (errno == ENOMEM) 405 return (PWU_NOMEM); 406 else { 407 /* algorithm problem? */ 408 syslog(LOG_AUTH | LOG_ALERT, 409 "passwdutil: crypt_gensalt " 410 "%m"); 411 return (PWU_UPDATE_FAILED); 412 } 413 } 414 nisbuf->pwd->pw_passwd = crypt(p->data.val_s, salt); 415 free(salt); 416 break; 417 case ATTR_UID: 418 nisbuf->pwd->pw_uid = (uid_t)p->data.val_i; 419 break; 420 case ATTR_GID: 421 nisbuf->pwd->pw_gid = (gid_t)p->data.val_i; 422 break; 423 case ATTR_AGE: 424 nisbuf->pwd->pw_age = p->data.val_s; 425 break; 426 case ATTR_COMMENT: 427 nisbuf->pwd->pw_comment = p->data.val_s; 428 break; 429 case ATTR_GECOS: 430 nisbuf->pwd->pw_gecos = p->data.val_s; 431 break; 432 case ATTR_HOMEDIR: 433 nisbuf->pwd->pw_dir = p->data.val_s; 434 break; 435 case ATTR_SHELL: 436 nisbuf->pwd->pw_shell = p->data.val_s; 437 break; 438 case ATTR_LSTCHG: 439 case ATTR_MIN: 440 case ATTR_MAX: 441 case ATTR_WARN: 442 case ATTR_INACT: 443 case ATTR_EXPIRE: 444 case ATTR_FLAG: 445 default: 446 break; 447 } 448 } 449 return (PWU_SUCCESS); 450 } 451 452 /* 453 * nis_putpwnam(name, oldpw, dummy, rep, buf) 454 * 455 * Update the NIS server. The passwd structure in buf will be sent to 456 * the server for user "name" authenticating with password "oldpw". 457 * The dummy parameter is a placeholder where for NIS+ where the 458 * old RPC password is passwd. 459 */ 460 /*ARGSUSED*/ 461 int 462 nis_putpwnam(char *name, char *oldpw, char *dummy, pwu_repository_t *rep, 463 void *buf) 464 { 465 nisbuf_t *nisbuf = (nisbuf_t *)buf; 466 struct yppasswd yppasswd; 467 struct netconfig *nconf; 468 int ok; 469 enum clnt_stat ans; 470 CLIENT *client; 471 struct timeval timeout; 472 473 if (strcmp(name, "root") == 0) 474 return (PWU_NOT_FOUND); 475 476 yppasswd.oldpass = oldpw ? oldpw : ""; 477 yppasswd.newpw = *nisbuf->pwd; 478 479 /* 480 * If we are privileged, we create a ticlts connection to the 481 * NIS server so that it can check our credentials 482 */ 483 if (nis_privileged(nisbuf)) { 484 nconf = getnetconfigent("ticlts"); 485 if (!nconf) { 486 syslog(LOG_ERR, 487 "passwdutil.so: Couldn't get netconfig entry"); 488 return (PWU_SYSTEM_ERROR); 489 } 490 client = clnt_tp_create(nisbuf->master, YPPASSWDPROG, 491 YPPASSWDVERS, nconf); 492 freenetconfigent(nconf); 493 } else { 494 /* Try IPv6 first */ 495 client = clnt_create(nisbuf->master, YPPASSWDPROG, 496 YPPASSWDVERS, "udp6"); 497 if (client == NULL) 498 client = clnt_create(nisbuf->master, YPPASSWDPROG, 499 YPPASSWDVERS, "udp"); 500 } 501 502 if (client == NULL) { 503 syslog(LOG_ERR, 504 "passwdutil.so: couldn't create client to YP master"); 505 return (PWU_SERVER_ERROR); 506 } 507 508 timeout.tv_usec = 0; 509 timeout.tv_sec = 55; /* ndp uses 55 seconds */ 510 511 ans = CLNT_CALL(client, YPPASSWDPROC_UPDATE, xdr_yppasswd, 512 (char *)&yppasswd, xdr_int, (char *)&ok, timeout); 513 514 if (nisbuf->pwd) 515 (void) free(nisbuf->pwd); 516 if (nisbuf->master) 517 (void) free(nisbuf->master); 518 if (nisbuf->scratch) 519 (void) free(nisbuf->scratch); 520 if (nisbuf->c2scratch) 521 (void) free(nisbuf->c2scratch); 522 523 (void) clnt_destroy(client); 524 525 if (ans != RPC_SUCCESS) { 526 return (PWU_UPDATE_FAILED); 527 } 528 529 /* These errors are obtained from the yppasswdd.c code */ 530 switch (ok) { 531 case 2: return (PWU_DENIED); 532 case 8: return (PWU_BUSY); 533 case 9: return (PWU_SERVER_ERROR); 534 case 4: return (PWU_NOT_FOUND); 535 case 3: return (PWU_NO_CHANGE); 536 case 7: return (PWU_DENIED); 537 case 0: return (PWU_SUCCESS); 538 default: return (PWU_SYSTEM_ERROR); 539 } 540 } 541