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.
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.
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.
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.
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.
25 * Sun Microsystems, Inc.
27 * Mountain View, California 94043
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.5 2005/11/13 12:27:04 swildner Exp $
34 * Copyright (c) 1988 by Sun Microsystems, Inc.
37 * auth_des.c, client-side implementation of DES authentication
42 #include <sys/cdefs.h>
43 #include <rpc/des_crypt.h>
44 #include <rpc/types.h>
47 #include <rpc/auth_des.h>
48 #include <netinet/in.h> /* XXX: just to get htonl() and ntohl() */
49 #include <sys/socket.h>
51 #include <rpcsvc/nis.h>
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
* );
59 #define MILLION 1000000L
60 #define RTIME_TIMEOUT 5 /* seconds to wait for sync */
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)
67 #define debug(msg) /*printf("%s\n", msg) */
70 * DES authenticator operations vector
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
= {
85 static bool_t
synchronize ( struct sockaddr
*, struct timeval
*);
88 * This struct is pointed to by the ah_private field of an "AUTH *"
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 */
113 * Create the client des authentication object
116 authdes_create(char *servername
, /* network name of server */
117 u_int window
, /* time to live */
118 struct sockaddr
*syncaddr
, /* optional addr of host to sync with */
119 des_block
*ckey
) /* optional conversation key to use*/
123 struct ad_private
*ad
;
124 char namebuf
[MAXNETNAMELEN
+1];
125 u_char pkey_data
[1024];
127 if (!getpublickey(servername
, pkey_data
))
131 * Allocate everything now
134 ad
= ALLOC(struct ad_private
);
137 ad
->ad_fullnamelen
= RNDUP(strlen(namebuf
));
138 ad
->ad_fullname
= (char *)mem_alloc(ad
->ad_fullnamelen
+ 1);
140 ad
->ad_servernamelen
= strlen(servername
);
141 ad
->ad_servername
= (char *)mem_alloc(ad
->ad_servernamelen
+ 1);
143 if (auth
== NULL
|| ad
== NULL
|| ad
->ad_fullname
== NULL
||
144 ad
->ad_servername
== NULL
) {
145 debug("authdes_create: out of memory");
150 * Set up private data
152 bcopy(namebuf
, ad
->ad_fullname
, ad
->ad_fullnamelen
+ 1);
153 bcopy(servername
, ad
->ad_servername
, ad
->ad_servernamelen
+ 1);
154 bcopy(pkey_data
, ad
->ad_pkey
, strlen(pkey_data
) + 1);
155 if (syncaddr
!= NULL
) {
156 ad
->ad_syncaddr
= *syncaddr
;
157 ad
->ad_dosync
= TRUE
;
159 ad
->ad_dosync
= FALSE
;
161 ad
->ad_window
= window
;
163 if (key_gendes(&auth
->ah_key
) < 0) {
164 debug("authdes_create: unable to gen conversation key");
168 auth
->ah_key
= *ckey
;
174 auth
->ah_cred
.oa_flavor
= AUTH_DES
;
175 auth
->ah_verf
.oa_flavor
= AUTH_DES
;
176 auth
->ah_ops
= &authdes_ops
;
177 auth
->ah_private
= (caddr_t
)ad
;
179 if (!authdes_refresh(auth
)) {
186 FREE(auth
, sizeof(AUTH
));
188 FREE(ad
, sizeof(struct ad_private
));
189 if (ad
->ad_fullname
!= NULL
)
190 FREE(ad
->ad_fullname
, ad
->ad_fullnamelen
+ 1);
191 if (ad
->ad_servername
!= NULL
)
192 FREE(ad
->ad_servername
, ad
->ad_servernamelen
+ 1);
197 * Slightly modified version of authdes_create which takes the public key
198 * of the server principal as an argument. This spares us a call to
199 * getpublickey() which in the nameserver context can cause a deadlock.
202 authdes_pk_create(char *servername
, /* network name of server */
203 netobj
*pkey
, /* public key of server */
204 u_int window
, /* time to live */
205 char *timehost
, /* optional hostname to sync with */
206 des_block
*ckey
, /* optional conversation key to use */
207 nis_server
*srvr
) /* optional NIS+ server struct */
210 struct ad_private
*ad
;
211 char namebuf
[MAXNETNAMELEN
+1];
214 * Allocate everything now
218 debug("authdes_pk_create: out of memory");
221 ad
= ALLOC(struct ad_private
);
223 debug("authdes_pk_create: out of memory");
226 ad
->ad_fullname
= ad
->ad_servername
= NULL
; /* Sanity reasons */
227 ad
->ad_timehost
= NULL
;
230 ad
->ad_nis_srvr
= NULL
;
231 ad
->ad_timediff
.tv_sec
= 0;
232 ad
->ad_timediff
.tv_usec
= 0;
233 memcpy(ad
->ad_pkey
, pkey
->n_bytes
, pkey
->n_len
);
234 if (!getnetname(namebuf
))
236 ad
->ad_fullnamelen
= RNDUP((u_int
) strlen(namebuf
));
237 ad
->ad_fullname
= (char *)mem_alloc(ad
->ad_fullnamelen
+ 1);
238 ad
->ad_servernamelen
= strlen(servername
);
239 ad
->ad_servername
= (char *)mem_alloc(ad
->ad_servernamelen
+ 1);
241 if (ad
->ad_fullname
== NULL
|| ad
->ad_servername
== NULL
) {
242 debug("authdes_pk_create: out of memory");
245 if (timehost
!= NULL
) {
246 ad
->ad_timehost
= (char *)mem_alloc(strlen(timehost
) + 1);
247 if (ad
->ad_timehost
== NULL
) {
248 debug("authdes_pk_create: out of memory");
251 memcpy(ad
->ad_timehost
, timehost
, strlen(timehost
) + 1);
252 ad
->ad_dosync
= TRUE
;
253 } else if (srvr
!= NULL
) {
254 ad
->ad_nis_srvr
= srvr
; /* transient */
255 ad
->ad_dosync
= TRUE
;
257 ad
->ad_dosync
= FALSE
;
259 memcpy(ad
->ad_fullname
, namebuf
, ad
->ad_fullnamelen
+ 1);
260 memcpy(ad
->ad_servername
, servername
, ad
->ad_servernamelen
+ 1);
261 ad
->ad_window
= window
;
263 if (key_gendes(&auth
->ah_key
) < 0) {
264 debug("authdes_pk_create: unable to gen conversation key");
268 auth
->ah_key
= *ckey
;
274 auth
->ah_cred
.oa_flavor
= AUTH_DES
;
275 auth
->ah_verf
.oa_flavor
= AUTH_DES
;
276 auth
->ah_ops
= &authdes_ops
;
277 auth
->ah_private
= (caddr_t
)ad
;
279 if (!authdes_refresh(auth
)) {
282 ad
->ad_nis_srvr
= NULL
; /* not needed any longer */
287 FREE(auth
, sizeof (AUTH
));
290 FREE(ad
->ad_fullname
, ad
->ad_fullnamelen
+ 1);
291 if (ad
->ad_servername
)
292 FREE(ad
->ad_servername
, ad
->ad_servernamelen
+ 1);
294 FREE(ad
->ad_timehost
, strlen(ad
->ad_timehost
) + 1);
299 FREE(ad
, sizeof (struct ad_private
));
304 * Implement the five authentication operations
313 authdes_nextverf(AUTH
*auth
)
315 /* what the heck am I supposed to do??? */
324 authdes_marshal(AUTH
*auth
, XDR
*xdrs
)
326 struct ad_private
*ad
= AUTH_PRIVATE(auth
);
327 struct authdes_cred
*cred
= &ad
->ad_cred
;
328 struct authdes_verf
*verf
= &ad
->ad_verf
;
329 des_block cryptbuf
[2];
336 * Figure out the "time", accounting for any time difference
337 * with the server if necessary.
339 gettimeofday(&ad
->ad_timestamp
, (struct timezone
*)NULL
);
340 ad
->ad_timestamp
.tv_sec
+= ad
->ad_timediff
.tv_sec
;
341 ad
->ad_timestamp
.tv_usec
+= ad
->ad_timediff
.tv_usec
;
342 if (ad
->ad_timestamp
.tv_usec
>= MILLION
) {
343 ad
->ad_timestamp
.tv_usec
-= MILLION
;
344 ad
->ad_timestamp
.tv_sec
+= 1;
348 * XDR the timestamp and possibly some other things, then
351 ixdr
= (int32_t *)cryptbuf
;
352 IXDR_PUT_LONG(ixdr
, ad
->ad_timestamp
.tv_sec
);
353 IXDR_PUT_LONG(ixdr
, ad
->ad_timestamp
.tv_usec
);
354 if (ad
->ad_cred
.adc_namekind
== ADN_FULLNAME
) {
355 IXDR_PUT_U_LONG(ixdr
, ad
->ad_window
);
356 IXDR_PUT_U_LONG(ixdr
, ad
->ad_window
- 1);
357 ivec
.key
.high
= ivec
.key
.low
= 0;
358 status
= cbc_crypt((char *)&auth
->ah_key
, (char *)cryptbuf
,
359 2*sizeof(des_block
), DES_ENCRYPT
| DES_HW
, (char *)&ivec
);
361 status
= ecb_crypt((char *)&auth
->ah_key
, (char *)cryptbuf
,
362 sizeof(des_block
), DES_ENCRYPT
| DES_HW
);
364 if (DES_FAILED(status
)) {
365 debug("authdes_marshal: DES encryption failure");
368 ad
->ad_verf
.adv_xtimestamp
= cryptbuf
[0];
369 if (ad
->ad_cred
.adc_namekind
== ADN_FULLNAME
) {
370 ad
->ad_cred
.adc_fullname
.window
= cryptbuf
[1].key
.high
;
371 ad
->ad_verf
.adv_winverf
= cryptbuf
[1].key
.low
;
373 ad
->ad_cred
.adc_nickname
= ad
->ad_nickname
;
374 ad
->ad_verf
.adv_winverf
= 0;
378 * Serialize the credential and verifier into opaque
379 * authentication data.
381 if (ad
->ad_cred
.adc_namekind
== ADN_FULLNAME
) {
382 len
= ((1 + 1 + 2 + 1)*BYTES_PER_XDR_UNIT
+ ad
->ad_fullnamelen
);
384 len
= (1 + 1)*BYTES_PER_XDR_UNIT
;
387 if ((ixdr
= xdr_inline(xdrs
, 2*BYTES_PER_XDR_UNIT
))) {
388 IXDR_PUT_LONG(ixdr
, AUTH_DES
);
389 IXDR_PUT_LONG(ixdr
, len
);
391 ATTEMPT(xdr_putlong(xdrs
, (long *)&auth
->ah_cred
.oa_flavor
));
392 ATTEMPT(xdr_putlong(xdrs
, &len
));
394 ATTEMPT(xdr_authdes_cred(xdrs
, cred
));
396 len
= (2 + 1)*BYTES_PER_XDR_UNIT
;
397 if ((ixdr
= xdr_inline(xdrs
, 2*BYTES_PER_XDR_UNIT
))) {
398 IXDR_PUT_LONG(ixdr
, AUTH_DES
);
399 IXDR_PUT_LONG(ixdr
, len
);
401 ATTEMPT(xdr_putlong(xdrs
, (long *)&auth
->ah_verf
.oa_flavor
));
402 ATTEMPT(xdr_putlong(xdrs
, &len
));
404 ATTEMPT(xdr_authdes_verf(xdrs
, verf
));
413 authdes_validate(AUTH
*auth
, struct opaque_auth
*rverf
)
415 struct ad_private
*ad
= AUTH_PRIVATE(auth
);
416 struct authdes_verf verf
;
420 if (rverf
->oa_length
!= (2 + 1) * BYTES_PER_XDR_UNIT
) {
423 ixdr
= (u_long
*)rverf
->oa_base
;
424 verf
.adv_xtimestamp
.key
.high
= (u_long
)*ixdr
++;
425 verf
.adv_xtimestamp
.key
.low
= (u_long
)*ixdr
++;
426 verf
.adv_int_u
= (u_long
)*ixdr
++; /* nickname not XDR'd ! */
429 * Decrypt the timestamp
431 status
= ecb_crypt((char *)&auth
->ah_key
, (char *)&verf
.adv_xtimestamp
,
432 sizeof(des_block
), DES_DECRYPT
| DES_HW
);
434 if (DES_FAILED(status
)) {
435 debug("authdes_validate: DES decryption failure");
440 * xdr the decrypted timestamp
442 ixdr
= (u_long
*)verf
.adv_xtimestamp
.c
;
443 verf
.adv_timestamp
.tv_sec
= IXDR_GET_LONG(ixdr
) + 1;
444 verf
.adv_timestamp
.tv_usec
= IXDR_GET_LONG(ixdr
);
449 if (bcmp((char *)&ad
->ad_timestamp
, (char *)&verf
.adv_timestamp
,
450 sizeof(struct timeval
)) != 0) {
451 debug("authdes_validate: verifier mismatch\n");
456 * We have a nickname now, let's use it
458 ad
->ad_nickname
= verf
.adv_nickname
;
459 ad
->ad_cred
.adc_namekind
= ADN_NICKNAME
;
467 authdes_refresh(AUTH
*auth
)
469 struct ad_private
*ad
= AUTH_PRIVATE(auth
);
470 struct authdes_cred
*cred
= &ad
->ad_cred
;
475 !synchronize(&ad
->ad_syncaddr
, &ad
->ad_timediff
)) {
477 !__rpc_get_time_offset(&ad
->ad_timediff
,ad
->ad_nis_srvr
,
478 ad
->ad_timehost
, &(ad
->ad_uaddr
),
479 (struct sockaddr_in
*)&(ad
->ad_syncaddr
))) {
482 * Hope the clocks are synced!
484 ad
->ad_timediff
.tv_sec
= ad
->ad_timediff
.tv_usec
= 0;
486 debug("authdes_refresh: unable to synchronize with server");
488 ad
->ad_xkey
= auth
->ah_key
;
489 pkey
.n_bytes
= (char *)(ad
->ad_pkey
);
490 pkey
.n_len
= strlen((char *)ad
->ad_pkey
) + 1;
491 if (key_encryptsession_pk(ad
->ad_servername
, &pkey
, &ad
->ad_xkey
) < 0) {
492 debug("authdes_create: unable to encrypt conversation key");
495 cred
->adc_fullname
.key
= ad
->ad_xkey
;
496 cred
->adc_namekind
= ADN_FULLNAME
;
497 cred
->adc_fullname
.name
= ad
->ad_fullname
;
506 authdes_destroy(AUTH
*auth
)
508 struct ad_private
*ad
= AUTH_PRIVATE(auth
);
510 FREE(ad
->ad_fullname
, ad
->ad_fullnamelen
+ 1);
511 FREE(ad
->ad_servername
, ad
->ad_servernamelen
+ 1);
512 FREE(ad
, sizeof(struct ad_private
));
513 FREE(auth
, sizeof(AUTH
));
519 * Synchronize with the server at the given address, that is,
520 * adjust timep to reflect the delta between our clocks
523 synchronize(struct sockaddr
*syncaddr
, struct timeval
*timep
)
525 struct timeval mytime
;
526 struct timeval timeout
;
528 timeout
.tv_sec
= RTIME_TIMEOUT
;
530 if (rtime((struct sockaddr_in
*)syncaddr
, timep
, NULL
/*&timeout*/) < 0) {
533 gettimeofday(&mytime
, (struct timezone
*)NULL
);
534 timep
->tv_sec
-= mytime
.tv_sec
;
535 if (mytime
.tv_usec
> timep
->tv_usec
) {
537 timep
->tv_usec
+= MILLION
;
539 timep
->tv_usec
-= mytime
.tv_usec
;