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