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