1 /* 2 * Sun RPC is a product of Sun Microsystems, Inc. and is provided for 3 * unrestricted use provided that this legend is included on all tape 4 * media and as a part of the software program in whole or part. Users 5 * may copy or modify Sun RPC without charge, but are not authorized 6 * to license or distribute it to anyone else except as part of a product or 7 * program developed by the user. 8 * 9 * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE 10 * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR 11 * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. 12 * 13 * Sun RPC is provided with no support and without any obligation on the 14 * part of Sun Microsystems, Inc. to assist in its use, correction, 15 * modification or enhancement. 16 * 17 * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE 18 * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC 19 * OR ANY PART THEREOF. 20 * 21 * In no event will Sun Microsystems, Inc. be liable for any lost revenue 22 * or profits or other special, indirect and consequential damages, even if 23 * Sun has been advised of the possibility of such damages. 24 * 25 * Sun Microsystems, Inc. 26 * 2550 Garcia Avenue 27 * Mountain View, California 94043 28 * 29 * @(#)auth_des.c 2.2 88/07/29 4.0 RPCSRC; from 1.9 88/02/08 SMI 30 * $FreeBSD: src/lib/libc/rpc/auth_des.c,v 1.3 1999/08/28 00:00:32 peter Exp $ 31 * $DragonFly: src/lib/libc/rpc/auth_des.c,v 1.4 2004/10/25 19:38:01 drhodus Exp $ 32 */ 33 /* 34 * Copyright (c) 1988 by Sun Microsystems, Inc. 35 */ 36 /* 37 * auth_des.c, client-side implementation of DES authentication 38 */ 39 #include <string.h> 40 #include <stdlib.h> 41 #include <unistd.h> 42 #include <sys/cdefs.h> 43 #include <rpc/des_crypt.h> 44 #include <rpc/types.h> 45 #include <rpc/xdr.h> 46 #include <rpc/auth.h> 47 #include <rpc/auth_des.h> 48 #include <netinet/in.h> /* XXX: just to get htonl() and ntohl() */ 49 #include <sys/socket.h> 50 #undef NIS 51 #include <rpcsvc/nis.h> 52 53 extern bool_t __rpc_get_time_offset ( struct timeval *, nis_server *, 54 char *, char **, struct sockaddr_in * ); 55 extern int rtime ( struct sockaddr_in *, struct timeval *, struct timeval *); 56 extern bool_t xdr_authdes_cred ( XDR *, struct authdes_cred * ); 57 extern bool_t xdr_authdes_verf ( XDR *, struct authdes_verf * ); 58 59 #define MILLION 1000000L 60 #define RTIME_TIMEOUT 5 /* seconds to wait for sync */ 61 62 #define AUTH_PRIVATE(auth) (struct ad_private *) auth->ah_private 63 #define ALLOC(object_type) (object_type *) mem_alloc(sizeof(object_type)) 64 #define FREE(ptr, size) mem_free((char *)(ptr), (int) size) 65 #define ATTEMPT(xdr_op) if (!(xdr_op)) return (FALSE) 66 67 #define debug(msg) /*printf("%s\n", msg) */ 68 69 /* 70 * DES authenticator operations vector 71 */ 72 static void authdes_nextverf(); 73 static bool_t authdes_marshal(); 74 static bool_t authdes_validate(); 75 static bool_t authdes_refresh(); 76 static void authdes_destroy(); 77 static struct auth_ops authdes_ops = { 78 authdes_nextverf, 79 authdes_marshal, 80 authdes_validate, 81 authdes_refresh, 82 authdes_destroy 83 }; 84 #ifdef foo 85 static bool_t synchronize ( struct sockaddr *, struct timeval *); 86 #endif 87 /* 88 * This struct is pointed to by the ah_private field of an "AUTH *" 89 */ 90 struct ad_private { 91 char *ad_fullname; /* client's full name */ 92 u_int ad_fullnamelen; /* length of name, rounded up */ 93 char *ad_servername; /* server's full name */ 94 u_int ad_servernamelen; /* length of name, rounded up */ 95 u_int ad_window; /* client specified window */ 96 bool_t ad_dosync; /* synchronize? */ 97 struct sockaddr ad_syncaddr; /* remote host to synch with */ 98 char *ad_timehost; /* remote host to synch with */ 99 struct timeval ad_timediff; /* server's time - client's time */ 100 u_long ad_nickname; /* server's nickname for client */ 101 struct authdes_cred ad_cred; /* storage for credential */ 102 struct authdes_verf ad_verf; /* storage for verifier */ 103 struct timeval ad_timestamp; /* timestamp sent */ 104 des_block ad_xkey; /* encrypted conversation key */ 105 u_char ad_pkey[1024]; /* Server's actual public key */ 106 char *ad_netid; /* Timehost netid */ 107 char *ad_uaddr; /* Timehost uaddr */ 108 nis_server *ad_nis_srvr; /* NIS+ server struct */ 109 }; 110 111 112 /* 113 * Create the client des authentication object 114 */ 115 AUTH * 116 authdes_create(servername, window, syncaddr, ckey) 117 char *servername; /* network name of server */ 118 u_int window; /* time to live */ 119 struct sockaddr *syncaddr; /* optional addr of host to sync with */ 120 des_block *ckey; /* optional conversation key to use*/ 121 { 122 123 AUTH *auth; 124 struct ad_private *ad; 125 char namebuf[MAXNETNAMELEN+1]; 126 u_char pkey_data[1024]; 127 128 if (!getpublickey(servername, pkey_data)) 129 return(NULL); 130 131 /* 132 * Allocate everything now 133 */ 134 auth = ALLOC(AUTH); 135 ad = ALLOC(struct ad_private); 136 (void) getnetname(namebuf); 137 138 ad->ad_fullnamelen = RNDUP(strlen(namebuf)); 139 ad->ad_fullname = (char *)mem_alloc(ad->ad_fullnamelen + 1); 140 141 ad->ad_servernamelen = strlen(servername); 142 ad->ad_servername = (char *)mem_alloc(ad->ad_servernamelen + 1); 143 144 if (auth == NULL || ad == NULL || ad->ad_fullname == NULL || 145 ad->ad_servername == NULL) { 146 debug("authdes_create: out of memory"); 147 goto failed; 148 } 149 150 /* 151 * Set up private data 152 */ 153 bcopy(namebuf, ad->ad_fullname, ad->ad_fullnamelen + 1); 154 bcopy(servername, ad->ad_servername, ad->ad_servernamelen + 1); 155 bcopy(pkey_data, ad->ad_pkey, strlen(pkey_data) + 1); 156 if (syncaddr != NULL) { 157 ad->ad_syncaddr = *syncaddr; 158 ad->ad_dosync = TRUE; 159 } else { 160 ad->ad_dosync = FALSE; 161 } 162 ad->ad_window = window; 163 if (ckey == NULL) { 164 if (key_gendes(&auth->ah_key) < 0) { 165 debug("authdes_create: unable to gen conversation key"); 166 return (NULL); 167 } 168 } else { 169 auth->ah_key = *ckey; 170 } 171 172 /* 173 * Set up auth handle 174 */ 175 auth->ah_cred.oa_flavor = AUTH_DES; 176 auth->ah_verf.oa_flavor = AUTH_DES; 177 auth->ah_ops = &authdes_ops; 178 auth->ah_private = (caddr_t)ad; 179 180 if (!authdes_refresh(auth)) { 181 goto failed; 182 } 183 return (auth); 184 185 failed: 186 if (auth != NULL) 187 FREE(auth, sizeof(AUTH)); 188 if (ad != NULL) 189 FREE(ad, sizeof(struct ad_private)); 190 if (ad->ad_fullname != NULL) 191 FREE(ad->ad_fullname, ad->ad_fullnamelen + 1); 192 if (ad->ad_servername != NULL) 193 FREE(ad->ad_servername, ad->ad_servernamelen + 1); 194 return (NULL); 195 } 196 197 /* 198 * Slightly modified version of authdes_create which takes the public key 199 * of the server principal as an argument. This spares us a call to 200 * getpublickey() which in the nameserver context can cause a deadlock. 201 */ 202 AUTH * 203 authdes_pk_create(servername, pkey, window, timehost, ckey, srvr) 204 char *servername; /* network name of server */ 205 netobj *pkey; /* public key of server */ 206 u_int window; /* time to live */ 207 char *timehost; /* optional hostname to sync with */ 208 des_block *ckey; /* optional conversation key to use */ 209 nis_server *srvr; /* optional NIS+ server struct */ 210 { 211 AUTH *auth; 212 struct ad_private *ad; 213 char namebuf[MAXNETNAMELEN+1]; 214 215 /* 216 * Allocate everything now 217 */ 218 auth = ALLOC(AUTH); 219 if (auth == NULL) { 220 debug("authdes_pk_create: out of memory"); 221 return (NULL); 222 } 223 ad = ALLOC(struct ad_private); 224 if (ad == NULL) { 225 debug("authdes_pk_create: out of memory"); 226 goto failed; 227 } 228 ad->ad_fullname = ad->ad_servername = NULL; /* Sanity reasons */ 229 ad->ad_timehost = NULL; 230 ad->ad_netid = NULL; 231 ad->ad_uaddr = NULL; 232 ad->ad_nis_srvr = NULL; 233 ad->ad_timediff.tv_sec = 0; 234 ad->ad_timediff.tv_usec = 0; 235 memcpy(ad->ad_pkey, pkey->n_bytes, pkey->n_len); 236 if (!getnetname(namebuf)) 237 goto failed; 238 ad->ad_fullnamelen = RNDUP((u_int) strlen(namebuf)); 239 ad->ad_fullname = (char *)mem_alloc(ad->ad_fullnamelen + 1); 240 ad->ad_servernamelen = strlen(servername); 241 ad->ad_servername = (char *)mem_alloc(ad->ad_servernamelen + 1); 242 243 if (ad->ad_fullname == NULL || ad->ad_servername == NULL) { 244 debug("authdes_pk_create: out of memory"); 245 goto failed; 246 } 247 if (timehost != NULL) { 248 ad->ad_timehost = (char *)mem_alloc(strlen(timehost) + 1); 249 if (ad->ad_timehost == NULL) { 250 debug("authdes_pk_create: out of memory"); 251 goto failed; 252 } 253 memcpy(ad->ad_timehost, timehost, strlen(timehost) + 1); 254 ad->ad_dosync = TRUE; 255 } else if (srvr != NULL) { 256 ad->ad_nis_srvr = srvr; /* transient */ 257 ad->ad_dosync = TRUE; 258 } else { 259 ad->ad_dosync = FALSE; 260 } 261 memcpy(ad->ad_fullname, namebuf, ad->ad_fullnamelen + 1); 262 memcpy(ad->ad_servername, servername, ad->ad_servernamelen + 1); 263 ad->ad_window = window; 264 if (ckey == NULL) { 265 if (key_gendes(&auth->ah_key) < 0) { 266 debug("authdes_pk_create: unable to gen conversation key"); 267 goto failed; 268 } 269 } else { 270 auth->ah_key = *ckey; 271 } 272 273 /* 274 * Set up auth handle 275 */ 276 auth->ah_cred.oa_flavor = AUTH_DES; 277 auth->ah_verf.oa_flavor = AUTH_DES; 278 auth->ah_ops = &authdes_ops; 279 auth->ah_private = (caddr_t)ad; 280 281 if (!authdes_refresh(auth)) { 282 goto failed; 283 } 284 ad->ad_nis_srvr = NULL; /* not needed any longer */ 285 return (auth); 286 287 failed: 288 if (auth) 289 FREE(auth, sizeof (AUTH)); 290 if (ad) { 291 if (ad->ad_fullname) 292 FREE(ad->ad_fullname, ad->ad_fullnamelen + 1); 293 if (ad->ad_servername) 294 FREE(ad->ad_servername, ad->ad_servernamelen + 1); 295 if (ad->ad_timehost) 296 FREE(ad->ad_timehost, strlen(ad->ad_timehost) + 1); 297 if (ad->ad_netid) 298 free(ad->ad_netid); 299 if (ad->ad_uaddr) 300 free(ad->ad_uaddr); 301 FREE(ad, sizeof (struct ad_private)); 302 } 303 return (NULL); 304 } 305 /* 306 * Implement the five authentication operations 307 */ 308 309 310 /* 311 * 1. Next Verifier 312 */ 313 /*ARGSUSED*/ 314 static void 315 authdes_nextverf(auth) 316 AUTH *auth; 317 { 318 /* what the heck am I supposed to do??? */ 319 } 320 321 322 323 /* 324 * 2. Marshal 325 */ 326 static bool_t 327 authdes_marshal(auth, xdrs) 328 AUTH *auth; 329 XDR *xdrs; 330 { 331 struct ad_private *ad = AUTH_PRIVATE(auth); 332 struct authdes_cred *cred = &ad->ad_cred; 333 struct authdes_verf *verf = &ad->ad_verf; 334 des_block cryptbuf[2]; 335 des_block ivec; 336 int status; 337 long len; 338 int32_t *ixdr; 339 340 /* 341 * Figure out the "time", accounting for any time difference 342 * with the server if necessary. 343 */ 344 (void) gettimeofday(&ad->ad_timestamp, (struct timezone *)NULL); 345 ad->ad_timestamp.tv_sec += ad->ad_timediff.tv_sec; 346 ad->ad_timestamp.tv_usec += ad->ad_timediff.tv_usec; 347 if (ad->ad_timestamp.tv_usec >= MILLION) { 348 ad->ad_timestamp.tv_usec -= MILLION; 349 ad->ad_timestamp.tv_sec += 1; 350 } 351 352 /* 353 * XDR the timestamp and possibly some other things, then 354 * encrypt them. 355 */ 356 ixdr = (int32_t *)cryptbuf; 357 IXDR_PUT_LONG(ixdr, ad->ad_timestamp.tv_sec); 358 IXDR_PUT_LONG(ixdr, ad->ad_timestamp.tv_usec); 359 if (ad->ad_cred.adc_namekind == ADN_FULLNAME) { 360 IXDR_PUT_U_LONG(ixdr, ad->ad_window); 361 IXDR_PUT_U_LONG(ixdr, ad->ad_window - 1); 362 ivec.key.high = ivec.key.low = 0; 363 status = cbc_crypt((char *)&auth->ah_key, (char *)cryptbuf, 364 2*sizeof(des_block), DES_ENCRYPT | DES_HW, (char *)&ivec); 365 } else { 366 status = ecb_crypt((char *)&auth->ah_key, (char *)cryptbuf, 367 sizeof(des_block), DES_ENCRYPT | DES_HW); 368 } 369 if (DES_FAILED(status)) { 370 debug("authdes_marshal: DES encryption failure"); 371 return (FALSE); 372 } 373 ad->ad_verf.adv_xtimestamp = cryptbuf[0]; 374 if (ad->ad_cred.adc_namekind == ADN_FULLNAME) { 375 ad->ad_cred.adc_fullname.window = cryptbuf[1].key.high; 376 ad->ad_verf.adv_winverf = cryptbuf[1].key.low; 377 } else { 378 ad->ad_cred.adc_nickname = ad->ad_nickname; 379 ad->ad_verf.adv_winverf = 0; 380 } 381 382 /* 383 * Serialize the credential and verifier into opaque 384 * authentication data. 385 */ 386 if (ad->ad_cred.adc_namekind == ADN_FULLNAME) { 387 len = ((1 + 1 + 2 + 1)*BYTES_PER_XDR_UNIT + ad->ad_fullnamelen); 388 } else { 389 len = (1 + 1)*BYTES_PER_XDR_UNIT; 390 } 391 392 if ((ixdr = xdr_inline(xdrs, 2*BYTES_PER_XDR_UNIT))) { 393 IXDR_PUT_LONG(ixdr, AUTH_DES); 394 IXDR_PUT_LONG(ixdr, len); 395 } else { 396 ATTEMPT(xdr_putlong(xdrs, (long *)&auth->ah_cred.oa_flavor)); 397 ATTEMPT(xdr_putlong(xdrs, &len)); 398 } 399 ATTEMPT(xdr_authdes_cred(xdrs, cred)); 400 401 len = (2 + 1)*BYTES_PER_XDR_UNIT; 402 if ((ixdr = xdr_inline(xdrs, 2*BYTES_PER_XDR_UNIT))) { 403 IXDR_PUT_LONG(ixdr, AUTH_DES); 404 IXDR_PUT_LONG(ixdr, len); 405 } else { 406 ATTEMPT(xdr_putlong(xdrs, (long *)&auth->ah_verf.oa_flavor)); 407 ATTEMPT(xdr_putlong(xdrs, &len)); 408 } 409 ATTEMPT(xdr_authdes_verf(xdrs, verf)); 410 return (TRUE); 411 } 412 413 414 /* 415 * 3. Validate 416 */ 417 static bool_t 418 authdes_validate(auth, rverf) 419 AUTH *auth; 420 struct opaque_auth *rverf; 421 { 422 struct ad_private *ad = AUTH_PRIVATE(auth); 423 struct authdes_verf verf; 424 int status; 425 u_long *ixdr; 426 427 if (rverf->oa_length != (2 + 1) * BYTES_PER_XDR_UNIT) { 428 return (FALSE); 429 } 430 ixdr = (u_long *)rverf->oa_base; 431 verf.adv_xtimestamp.key.high = (u_long)*ixdr++; 432 verf.adv_xtimestamp.key.low = (u_long)*ixdr++; 433 verf.adv_int_u = (u_long)*ixdr++; /* nickname not XDR'd ! */ 434 435 /* 436 * Decrypt the timestamp 437 */ 438 status = ecb_crypt((char *)&auth->ah_key, (char *)&verf.adv_xtimestamp, 439 sizeof(des_block), DES_DECRYPT | DES_HW); 440 441 if (DES_FAILED(status)) { 442 debug("authdes_validate: DES decryption failure"); 443 return (FALSE); 444 } 445 446 /* 447 * xdr the decrypted timestamp 448 */ 449 ixdr = (u_long *)verf.adv_xtimestamp.c; 450 verf.adv_timestamp.tv_sec = IXDR_GET_LONG(ixdr) + 1; 451 verf.adv_timestamp.tv_usec = IXDR_GET_LONG(ixdr); 452 453 /* 454 * validate 455 */ 456 if (bcmp((char *)&ad->ad_timestamp, (char *)&verf.adv_timestamp, 457 sizeof(struct timeval)) != 0) { 458 debug("authdes_validate: verifier mismatch\n"); 459 return (FALSE); 460 } 461 462 /* 463 * We have a nickname now, let's use it 464 */ 465 ad->ad_nickname = verf.adv_nickname; 466 ad->ad_cred.adc_namekind = ADN_NICKNAME; 467 return (TRUE); 468 } 469 470 /* 471 * 4. Refresh 472 */ 473 static bool_t 474 authdes_refresh(auth) 475 AUTH *auth; 476 { 477 struct ad_private *ad = AUTH_PRIVATE(auth); 478 struct authdes_cred *cred = &ad->ad_cred; 479 netobj pkey; 480 481 if (ad->ad_dosync && 482 #ifdef old 483 !synchronize(&ad->ad_syncaddr, &ad->ad_timediff)) { 484 #else 485 !__rpc_get_time_offset(&ad->ad_timediff,ad->ad_nis_srvr, 486 ad->ad_timehost, &(ad->ad_uaddr), 487 (struct sockaddr_in *)&(ad->ad_syncaddr))) { 488 #endif 489 /* 490 * Hope the clocks are synced! 491 */ 492 ad->ad_timediff.tv_sec = ad->ad_timediff.tv_usec = 0; 493 ad->ad_dosync = 0; 494 debug("authdes_refresh: unable to synchronize with server"); 495 } 496 ad->ad_xkey = auth->ah_key; 497 pkey.n_bytes = (char *)(ad->ad_pkey); 498 pkey.n_len = strlen((char *)ad->ad_pkey) + 1; 499 if (key_encryptsession_pk(ad->ad_servername, &pkey, &ad->ad_xkey) < 0) { 500 debug("authdes_create: unable to encrypt conversation key"); 501 return (FALSE); 502 } 503 cred->adc_fullname.key = ad->ad_xkey; 504 cred->adc_namekind = ADN_FULLNAME; 505 cred->adc_fullname.name = ad->ad_fullname; 506 return (TRUE); 507 } 508 509 510 /* 511 * 5. Destroy 512 */ 513 static void 514 authdes_destroy(auth) 515 AUTH *auth; 516 { 517 struct ad_private *ad = AUTH_PRIVATE(auth); 518 519 FREE(ad->ad_fullname, ad->ad_fullnamelen + 1); 520 FREE(ad->ad_servername, ad->ad_servernamelen + 1); 521 FREE(ad, sizeof(struct ad_private)); 522 FREE(auth, sizeof(AUTH)); 523 } 524 525 526 #ifdef old 527 /* 528 * Synchronize with the server at the given address, that is, 529 * adjust timep to reflect the delta between our clocks 530 */ 531 static bool_t 532 synchronize(syncaddr, timep) 533 struct sockaddr *syncaddr; 534 struct timeval *timep; 535 { 536 struct timeval mytime; 537 struct timeval timeout; 538 539 timeout.tv_sec = RTIME_TIMEOUT; 540 timeout.tv_usec = 0; 541 if (rtime((struct sockaddr_in *)syncaddr, timep, NULL /*&timeout*/) < 0) { 542 return (FALSE); 543 } 544 (void) gettimeofday(&mytime, (struct timezone *)NULL); 545 timep->tv_sec -= mytime.tv_sec; 546 if (mytime.tv_usec > timep->tv_usec) { 547 timep->tv_sec -= 1; 548 timep->tv_usec += MILLION; 549 } 550 timep->tv_usec -= mytime.tv_usec; 551 return (TRUE); 552 } 553 #endif 554