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 /* 27 * npd_clnt.c 28 * Contains all the client-side routines to communicate 29 * with the NIS+ passwd update deamon. 30 * 31 */ 32 33 #include <stdlib.h> 34 #include <syslog.h> 35 #include <string.h> 36 #include <shadow.h> 37 #include <rpc/rpc.h> 38 #include <rpc/xdr.h> 39 #include <rpc/des_crypt.h> 40 #include <mp.h> 41 #include <rpc/key_prot.h> 42 #include <rpcsvc/nis.h> 43 #include <rpcsvc/nispasswd.h> 44 #include <rpcsvc/nis_dhext.h> 45 #include <memory.h> 46 #include <sys/time.h> 47 #include <unistd.h> 48 #include <sys/types.h> 49 50 #define _NPD_PASSMAXLEN 16 51 52 extern bool_t __npd_ecb_crypt(uint32_t *, uint32_t *, 53 des_block *, unsigned int, unsigned int, 54 des_block *); 55 extern bool_t __npd_cbc_crypt(uint32_t *, char *, unsigned int, 56 npd_newpass *, unsigned int, unsigned int, 57 des_block *); 58 extern bool_t __npd2_cbc_crypt(uint32_t *, char *, unsigned int, 59 npd_newpass2 *, unsigned int, unsigned int, 60 des_block *); 61 rpcvers_t clnt_vers = NISPASSWD_VERS2; 62 63 /* 64 * Loop thru the NIS+ security cf entries until one DH(EXT) mech key is 65 * successfully extracted from the server DHEXT netobj. Copy the hex key 66 * string to newly allocated memory and return it's address in 'keybuf'. 67 * The caller must free this memory. Also, dup the key length and algtype 68 * "alias" string and return it's address in keystr (which the caller 69 * must also free on successful return). 70 * 71 * Policy: If no valid cf entries exist or if the entry is the "des" compat 72 * one, then try it and then end search. 73 * 74 * Returns TRUE on success and FALSE on failure. 75 */ 76 static bool_t 77 get_dhext_key( 78 netobj *pkey, /* in */ 79 char **keybuf, /* out */ 80 keylen_t *keylen, /* (bits) out */ 81 algtype_t *keyalgtype, /* out */ 82 char **keystr) /* out */ 83 { 84 mechanism_t **mechs; /* list of mechanisms */ 85 char *hexkey; /* hex public key */ 86 87 if (mechs = __nis_get_mechanisms(FALSE)) { 88 mechanism_t **mpp; 89 90 for (mpp = mechs; *mpp; mpp++) { 91 mechanism_t *mp = *mpp; 92 93 if (AUTH_DES_COMPAT_CHK(mp)) { 94 __nis_release_mechanisms(mechs); 95 goto try_auth_des; 96 } 97 if (! VALID_MECH_ENTRY(mp)) 98 continue; 99 100 if (hexkey = __nis_dhext_extract_pkey(pkey, 101 mp->keylen, mp->algtype)) { 102 if ((*keybuf = malloc(strlen(hexkey) + 1)) 103 == 0) { 104 syslog(LOG_ERR, "malloc failed"); 105 continue; /* try next mech */ 106 } 107 (void) strcpy(*keybuf, hexkey); 108 *keylen = mp->keylen; 109 *keyalgtype = mp->algtype; 110 *keystr = strdup(mp->alias); 111 __nis_release_mechanisms(mechs); 112 return (TRUE); 113 } else 114 continue; 115 } 116 __nis_release_mechanisms(mechs); 117 return (FALSE); 118 } else { 119 120 /* no valid cf mech entries or AUTH_DES compat entry found */ 121 try_auth_des: 122 if (hexkey = __nis_dhext_extract_pkey(pkey, 123 AUTH_DES_KEYLEN, AUTH_DES_ALGTYPE)) { 124 if ((*keybuf = malloc(strlen(hexkey) + 1)) == NULL) { 125 syslog(LOG_ERR, "malloc failed"); 126 return (FALSE); 127 } 128 (void) strcpy(*keybuf, hexkey); 129 *keylen = AUTH_DES_KEYLEN; 130 *keyalgtype = AUTH_DES_ALGTYPE; 131 *keystr = strdup(NIS_SEC_CF_DES_ALIAS); 132 return (TRUE); 133 } 134 } 135 return (FALSE); 136 } 137 138 139 140 /* 141 * given the domain return the client handle to the rpc.nispasswdd 142 * that I need to contact and the master_servers' publickey and the 143 * the key length and algtype "alias" string. 144 * 145 * returns TRUE on success and FALSE on failure. 146 * 147 * on successful return, caller must free the srv_pubkey buf and 148 * the keystr buf. 149 */ 150 bool_t 151 npd_makeclnthandle(domain, clnt, srv_pubkey, 152 srv_keylen, srv_keyalgtype, key_type) 153 char *domain; 154 CLIENT **clnt; /* out */ 155 char **srv_pubkey; /* buf to hold the pubkey; out */ 156 keylen_t *srv_keylen; /* server key lenth (bits); out */ 157 algtype_t *srv_keyalgtype; /* server key algorithm type; out */ 158 char **key_type; /* key length/algtype str buf; out */ 159 { 160 nis_server **srvs; /* servers that serve 'domain' */ 161 nis_server *master_srv; 162 char buf[NIS_MAXNAMELEN]; 163 CLIENT *tmpclnt = NULL; 164 rpcvers_t vers; 165 166 if (domain == NULL || *domain == '\0') 167 domain = nis_local_directory(); 168 169 /* strlen("org_dir.") + null + "." = 10 */ 170 if ((strlen(domain) + 10) > (size_t)NIS_MAXNAMELEN) 171 return (FALSE); 172 (void) snprintf(buf, sizeof (buf), "org_dir.%s", domain); 173 if (buf[strlen(buf) - 1] != '.') 174 (void) strcat(buf, "."); 175 176 srvs = nis_getservlist(buf); 177 if (srvs == NULL) { 178 /* can't find any of the servers that serve this domain */ 179 /* something is very wrong ! */ 180 syslog(LOG_ERR, 181 "can't get a list of servers for %s domain", 182 domain); 183 return (FALSE); 184 } 185 master_srv = srvs[0]; /* the first one is always the master */ 186 187 /* 188 * copy a publickey 189 */ 190 switch (master_srv->key_type) { 191 case NIS_PK_DHEXT: 192 if (!get_dhext_key(&(master_srv->pkey), srv_pubkey, 193 srv_keylen, srv_keyalgtype, 194 key_type)) { 195 syslog(LOG_WARNING, 196 "could not get a DHEXT public key for master server '%s'", 197 master_srv->name); 198 (void) nis_freeservlist(srvs); 199 return (FALSE); 200 } 201 break; 202 203 case NIS_PK_DH: 204 if ((*srv_pubkey = malloc(master_srv->pkey.n_len)) == NULL) { 205 syslog(LOG_ERR, "malloc failed"); 206 (void) nis_freeservlist(srvs); 207 return (FALSE); 208 } 209 (void) strcpy(*srv_pubkey, master_srv->pkey.n_bytes); 210 *srv_keylen = AUTH_DES_KEYLEN; 211 *srv_keyalgtype = AUTH_DES_ALGTYPE; 212 *key_type = strdup(AUTH_DES_AUTH_TYPE); 213 break; 214 215 case NIS_PK_NONE: 216 default: 217 /* server does not have a D-H key-pair */ 218 syslog(LOG_ERR, "no publickey for %s", master_srv->name); 219 (void) nis_freeservlist(srvs); 220 return (FALSE); 221 } 222 223 /* 224 * now that we have the universal addr for the master server, 225 * lets create the client handle to rpc.nispasswdd. 226 * always use VC and attempt to create an authenticated handle. 227 * nis_make_rpchandle() will attempt to use auth_des first, 228 * if user does not have D-H keys, then it will try auth_sys. 229 * sendsz and recvsz are 0 ==> choose defaults. 230 * 231 * First try NISPASSWD_VERS2. If it fails, fallback to NISPASSWD_VERS 232 */ 233 for (vers = NISPASSWD_VERS2; 234 (vers >= NISPASSWD_VERS) && (tmpclnt == NULL); vers--) { 235 tmpclnt = nis_make_rpchandle_gss_svc_ruid(master_srv, 0, 236 NISPASSWD_PROG, vers, ZMH_VC+ZMH_AUTH, 0, 237 0, NULL, NIS_SVCNAME_NISPASSWD); 238 clnt_vers = vers; 239 } 240 241 /* done with server list */ 242 (void) nis_freeservlist(srvs); 243 if (tmpclnt == NULL) { 244 /* 245 * error syslog'd by nis_make_rpchandle() 246 */ 247 return (FALSE); 248 } 249 *clnt = tmpclnt; 250 return (TRUE); 251 } 252 253 /* Default timeout can be changed using clnt_control() */ 254 static struct timeval TIMEOUT = { 55, 0 }; 255 256 /* 257 * initiate the passwd update request session by sending 258 * username, domainname, the generated public key and 259 * the callers' old passwd encrypted with the common DES key. 260 * if it succeeds, decrypt the identifier and randval sent in 261 * the response; otherwise return an appropriate error code. 262 */ 263 nispasswd_status 264 nispasswd_auth(user, domain, oldpass, u_pubkey, key_type, keylen, 265 algtype, deskeys, clnt, ident, randval, err) 266 char *user; /* user name */ 267 char *domain; /* domain */ 268 char *oldpass; /* clear old password */ 269 uchar_t *u_pubkey; /* users' public key */ 270 char *key_type; /* key len and alg type string */ 271 keylen_t keylen; /* user's public key length */ 272 algtype_t algtype; /* user's public key algorithm type */ 273 des_block *deskeys; /* the common DES key */ 274 CLIENT *clnt; /* client handle to rpc.nispasswdd */ 275 uint32_t *ident; /* ID, returned on first attempt */ 276 uint32_t *randval; /* R, returned on first attempt */ 277 int *err; /* error code, returned */ 278 { 279 npd_request req_arg; 280 nispasswd_authresult res; 281 des_block ivec; 282 unsigned char xpass[_NPD_PASSMAXLEN+1]; 283 unsigned char xpass2[__NPD2_MAXPASSBYTES+1]; 284 des_block cryptbuf; 285 int cryptstat; 286 int i; 287 288 if ((user == NULL || *user == '\0') || 289 (domain == NULL || *domain == '\0') || 290 (oldpass == NULL || *oldpass == '\0') || 291 (u_pubkey == NULL || *u_pubkey == '\0') || 292 (deskeys == (des_block *) NULL) || 293 (clnt == (CLIENT *) NULL)) { 294 *err = NPD_INVALIDARGS; 295 return (NPD_FAILED); 296 } 297 (void) memset((char *)&req_arg, 0, sizeof (req_arg)); 298 (void) memset((char *)&res, 0, sizeof (res)); 299 300 if (clnt_vers == NISPASSWD_VERS) { 301 /* encrypt the passwd with the common des key */ 302 if (strlen(oldpass) > (size_t)_NPD_PASSMAXLEN) { 303 *err = NPD_BUFTOOSMALL; 304 return (NPD_FAILED); 305 } 306 (void) strlcpy((char *)xpass, oldpass, sizeof (xpass)); 307 for (i = strlen(oldpass); i < _NPD_PASSMAXLEN; i++) 308 xpass[i] = '\0'; 309 310 ivec.key.high = ivec.key.low = 0; 311 if (AUTH_DES_KEY(keylen, algtype)) 312 cryptstat = cbc_crypt((char *)deskeys[0].c, 313 (char *)xpass, _NPD_PASSMAXLEN, 314 DES_ENCRYPT | DES_HW, (char *)&ivec); 315 else 316 cryptstat = __cbc_triple_crypt(deskeys, (char *)xpass, 317 _NPD_PASSMAXLEN, DES_ENCRYPT | DES_HW, 318 (char *)&ivec); 319 320 if (DES_FAILED(cryptstat)) { 321 *err = NPD_ENCRYPTFAIL; 322 return (NPD_FAILED); 323 } 324 } else { 325 /* encrypt the passwd with the common des key */ 326 if (strlen(oldpass) > (size_t)__NPD2_MAXPASSBYTES) { 327 *err = NPD_BUFTOOSMALL; 328 return (NPD_FAILED); 329 } 330 (void) strlcpy((char *)xpass2, oldpass, sizeof (xpass2)); 331 for (i = strlen(oldpass); i < __NPD2_MAXPASSBYTES; i++) 332 xpass2[i] = '\0'; 333 334 ivec.key.high = ivec.key.low = 0; 335 if (AUTH_DES_KEY(keylen, algtype)) 336 cryptstat = cbc_crypt((char *)deskeys[0].c, 337 (char *)xpass2, __NPD2_MAXPASSBYTES, 338 DES_ENCRYPT | DES_HW, (char *)&ivec); 339 else 340 cryptstat = __cbc_triple_crypt(deskeys, (char *)xpass2, 341 __NPD2_MAXPASSBYTES, 342 DES_ENCRYPT | DES_HW, (char *)&ivec); 343 344 if (DES_FAILED(cryptstat)) { 345 *err = NPD_ENCRYPTFAIL; 346 return (NPD_FAILED); 347 } 348 } 349 350 req_arg.username = user; 351 req_arg.domain = domain; 352 req_arg.key_type = key_type; 353 req_arg.user_pub_key.user_pub_key_len = 354 strlen((char *)u_pubkey) + 1; 355 req_arg.user_pub_key.user_pub_key_val = u_pubkey; 356 if (clnt_vers == NISPASSWD_VERS) { 357 req_arg.npd_authpass.npd_authpass_len = _NPD_PASSMAXLEN; 358 req_arg.npd_authpass.npd_authpass_val = xpass; 359 } else { 360 req_arg.npd_authpass.npd_authpass_len = __NPD2_MAXPASSBYTES; 361 req_arg.npd_authpass.npd_authpass_val = xpass2; 362 } 363 req_arg.ident = *ident; /* on re-tries ident is non-zero */ 364 365 if (clnt_call(clnt, NISPASSWD_AUTHENTICATE, 366 (xdrproc_t)xdr_npd_request, (caddr_t)&req_arg, 367 (xdrproc_t)xdr_nispasswd_authresult, (caddr_t)&res, 368 TIMEOUT) != RPC_SUCCESS) { 369 370 /* following msg is printed on stderr */ 371 (void) clnt_perror(clnt, 372 "authenticate call to rpc.nispasswdd failed"); 373 *err = NPD_SRVNOTRESP; 374 return (NPD_FAILED); 375 } 376 377 switch (res.status) { 378 case NPD_SUCCESS: 379 case NPD_TRYAGAIN: 380 /* 381 * decrypt the ident & randval 382 */ 383 cryptbuf.key.high = 384 ntohl(res.nispasswd_authresult_u.npd_verf.npd_xid); 385 cryptbuf.key.low = 386 ntohl(res.nispasswd_authresult_u.npd_verf.npd_xrandval); 387 388 if (! __npd_ecb_crypt(ident, randval, &cryptbuf, 389 sizeof (des_block), DES_DECRYPT, &(deskeys[0]))) { 390 *err = NPD_DECRYPTFAIL; 391 return (NPD_FAILED); 392 } 393 return (res.status); 394 395 case NPD_FAILED: 396 *err = res.nispasswd_authresult_u.npd_err; 397 return (NPD_FAILED); 398 default: 399 /* 400 * should never reach this case ! 401 */ 402 *err = NPD_SYSTEMERR; 403 return (NPD_FAILED); 404 } 405 /* NOTREACHED */ 406 } 407 408 /* 409 * authenticated the caller, now send the identifier; and the 410 * new password and the random value encrypted with the common 411 * DES key. Send any other changed password information in the 412 * clear. 413 */ 414 int 415 nispasswd_pass(clnt, ident, randval, deskey, newpass, gecos, shell, err, errlst) 416 CLIENT *clnt; /* client handle to rpc.nispasswdd */ 417 uint32_t ident; /* ID */ 418 uint32_t randval; /* R */ 419 des_block *deskey; /* common DES key */ 420 char *newpass; /* clear new password */ 421 char *gecos; /* gecos */ 422 char *shell; /* shell */ 423 int *err; /* error code, returned */ 424 nispasswd_error **errlst; /* error list on partial success, returned */ 425 { 426 npd_update send_arg; 427 npd_update2 send_arg2; 428 nispasswd_updresult result; 429 npd_newpass cryptbuf1; 430 npd_newpass2 cryptbuf2; 431 unsigned int tmp_xrval; 432 unsigned int tmp_npd_pad; 433 nispasswd_error *errl = NULL, *p; 434 char xnewpass[__NPD_MAXPASSBYTES+1]; 435 char xnewpass2[__NPD2_MAXPASSBYTES+1]; 436 437 if ((clnt == (CLIENT *) NULL) || 438 (deskey == (des_block *) NULL) || 439 (newpass == NULL || *newpass == '\0')) { 440 *err = NPD_INVALIDARGS; 441 return (NPD_FAILED); 442 } 443 444 if (clnt_vers == NISPASSWD_VERS) { 445 (void) memset((char *)&send_arg, 0, sizeof (send_arg)); 446 (void) memset((char *)&result, 0, sizeof (result)); 447 send_arg.ident = ident; 448 449 (void) strlcpy(xnewpass, newpass, sizeof (xnewpass)); 450 451 if (! __npd_cbc_crypt(&randval, xnewpass, strlen(xnewpass), 452 &cryptbuf1, _NPD_PASSMAXLEN, DES_ENCRYPT, deskey)) { 453 *err = NPD_ENCRYPTFAIL; 454 return (NPD_FAILED); 455 } 456 tmp_xrval = cryptbuf1.npd_xrandval; 457 cryptbuf1.npd_xrandval = htonl(tmp_xrval); 458 send_arg.xnewpass = cryptbuf1; 459 460 /* gecos */ 461 send_arg.pass_info.pw_gecos = gecos; 462 463 /* shell */ 464 send_arg.pass_info.pw_shell = shell; 465 466 if (clnt_call(clnt, NISPASSWD_UPDATE, 467 (xdrproc_t)xdr_npd_update, (caddr_t)&send_arg, 468 (xdrproc_t)xdr_nispasswd_updresult, (caddr_t)&result, 469 TIMEOUT) != RPC_SUCCESS) { 470 471 /* printed to stderr */ 472 (void) clnt_perror(clnt, 473 "update call to rpc.nispasswdd failed"); 474 *err = NPD_SRVNOTRESP; 475 return (NPD_FAILED); 476 } 477 478 } else { 479 (void) memset((char *)&send_arg2, 0, sizeof (send_arg2)); 480 (void) memset((char *)&result, 0, sizeof (result)); 481 send_arg2.ident = ident; 482 483 (void) strlcpy(xnewpass2, newpass, sizeof (xnewpass2)); 484 485 if (! __npd2_cbc_crypt(&randval, xnewpass2, strlen(xnewpass2), 486 &cryptbuf2, sizeof (cryptbuf2), DES_ENCRYPT, 487 deskey)) { 488 *err = NPD_ENCRYPTFAIL; 489 return (NPD_FAILED); 490 } 491 tmp_xrval = cryptbuf2.npd_xrandval; 492 tmp_npd_pad = cryptbuf2.npd_pad; 493 cryptbuf2.npd_xrandval = htonl(tmp_xrval); 494 cryptbuf2.npd_pad = htonl(tmp_npd_pad); 495 496 send_arg2.xnewpass = cryptbuf2; 497 /* gecos */ 498 send_arg2.pass_info.pw_gecos = gecos; 499 /* shell */ 500 send_arg2.pass_info.pw_shell = shell; 501 502 if (clnt_call(clnt, NISPASSWD_UPDATE, 503 (xdrproc_t)xdr_npd_update2, (caddr_t)&send_arg2, 504 (xdrproc_t)xdr_nispasswd_updresult, (caddr_t)&result, 505 TIMEOUT) != RPC_SUCCESS) { 506 507 /* printed to stderr */ 508 (void) clnt_perror(clnt, 509 "update call to rpc.nispasswdd failed"); 510 *err = NPD_SRVNOTRESP; 511 return (NPD_FAILED); 512 } 513 } 514 switch (result.status) { 515 case NPD_SUCCESS: 516 return (NPD_SUCCESS); 517 case NPD_PARTIALSUCCESS: 518 /* need to assign field/err code */ 519 errl = &result.nispasswd_updresult_u.reason; 520 if (errl == (struct nispasswd_error *)NULL) { 521 *err = NPD_SYSTEMERR; 522 return (NPD_FAILED); 523 } 524 *errlst = (nispasswd_error *) 525 calloc(1, sizeof (nispasswd_error)); 526 if (*errlst == (struct nispasswd_error *)NULL) { 527 *err = NPD_SYSTEMERR; 528 return (NPD_FAILED); 529 } 530 531 for (p = *errlst; errl != NULL; errl = errl->next) { 532 p->npd_field = errl->npd_field; 533 p->npd_code = errl->npd_code; 534 if (errl->next != NULL) { 535 p->next = (nispasswd_error *) 536 calloc(1, sizeof (nispasswd_error)); 537 p = p->next; 538 } else 539 p->next = (nispasswd_error *) NULL; 540 } 541 return (NPD_PARTIALSUCCESS); 542 case NPD_FAILED: 543 *err = result.nispasswd_updresult_u.npd_err; 544 return (NPD_FAILED); 545 default: 546 /* 547 * should never reach this case ! 548 */ 549 *err = NPD_SYSTEMERR; 550 return (NPD_FAILED); 551 } 552 } 553 554 void 555 __npd_free_errlist(list) 556 nispasswd_error *list; 557 { 558 nispasswd_error *p; 559 560 if (list == NULL) 561 return; 562 for (; list != NULL; list = p) { 563 p = list->next; 564 free(list); 565 } 566 list = NULL; 567 } 568