2 * Copyright (c) 1995-1997, 2003-2006 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the Institute nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 #include "rxkad_locl.h"
50 #ifndef KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS
51 #undef HAVE_KRB5 /* no heimdal, no kerberos support */
55 #include <openssl/rand.h>
58 /* Security object specific server data */
59 typedef struct rxkad_serv_class
{
60 struct rx_securityClass klass
;
61 rxkad_level min_level
;
63 int (*get_key
)(void *appl_data
, int kvno
, struct ktc_encryptionKey
*key
);
64 int (*user_ok
)(char *name
, int kvno
);
69 server_NewConnection(struct rx_securityClass
*obj
, struct rx_connection
*con
)
71 assert(con
->securityData
== 0);
73 con
->securityData
= (char *) osi_Alloc(sizeof(serv_con_data
));
74 memset(con
->securityData
, 0x0, sizeof(serv_con_data
));
80 server_Close(struct rx_securityClass
*obj
)
83 if (obj
->refCount
<= 0)
84 osi_Free(obj
, sizeof(rxkad_serv_class
));
90 server_DestroyConnection(struct rx_securityClass
*obj
,
91 struct rx_connection
*con
)
93 serv_con_data
*cdat
= (serv_con_data
*)con
->securityData
;
101 return server_Close(obj
);
105 * Check whether a connection authenticated properly.
106 * Zero is good (authentication succeeded).
110 server_CheckAuthentication(struct rx_securityClass
*obj
,
111 struct rx_connection
*con
)
113 serv_con_data
*cdat
= (serv_con_data
*) con
->securityData
;
116 return !cdat
->authenticated
;
122 * Select a nonce for later use.
126 server_CreateChallenge(struct rx_securityClass
*obj_
,
127 struct rx_connection
*con
)
129 rxkad_serv_class
*obj
= (rxkad_serv_class
*) obj_
;
130 serv_con_data
*cdat
= (serv_con_data
*) con
->securityData
;
132 /* Any good random numbers will do, no real need to use
133 * cryptographic techniques here */
137 RAND_pseudo_bytes((void *)rnd
, sizeof(rnd
));
138 cdat
->nonce
= rnd
[0] ^ rnd
[1];
144 des_random_key(&u
.k
);
145 cdat
->nonce
= u
.rnd
[0] ^ u
.rnd
[1];
148 cdat
->authenticated
= 0;
149 cdat
->cur_level
= obj
->min_level
;
154 * Wrap the nonce in a challenge packet.
158 server_GetChallenge(const struct rx_securityClass
*obj
,
159 const struct rx_connection
*con
,
160 struct rx_packet
*pkt
)
162 serv_con_data
*cdat
= (serv_con_data
*) con
->securityData
;
166 c
.version
= htonl(RXKAD_VERSION
);
167 c
.nonce
= htonl(cdat
->nonce
);
168 c
.min_level
= htonl((int32_t)cdat
->cur_level
);
169 c
.unused
= 0; /* Use this to hint client we understand krb5 tickets??? */
171 /* Stuff into packet */
172 if (rx_SlowWritePacket(pkt
, 0, sizeof(c
), &c
) != sizeof(c
))
173 return RXKADPACKETSHORT
;
174 rx_SetDataSize(pkt
, sizeof(c
));
180 decode_krb5_ticket(rxkad_serv_class
*obj
,
185 struct ktc_encryptionKey
*session_key
,
190 return RXKADBADTICKET
;
192 struct ktc_encryptionKey serv_key
; /* Service's secret key */
193 krb5_keyblock key
; /* Uses serv_key above */
196 krb5_principal principal
;
199 Ticket t5
; /* Must free */
200 EncTicketPart decr_part
; /* Must free */
201 krb5_context context
; /* Must free */
202 krb5_data plain
; /* Must free */
204 memset(&t5
, 0x0, sizeof(t5
));
205 memset(&decr_part
, 0x0, sizeof(decr_part
));
206 krb5_init_context(&context
);
207 krb5_data_zero(&plain
);
209 assert(serv_kvno
== RXKAD_TKT_TYPE_KERBEROS_V5
||
210 serv_kvno
== RXKAD_TKT_TYPE_KERBEROS_V5_ENCPART_ONLY
);
212 if (serv_kvno
== RXKAD_TKT_TYPE_KERBEROS_V5_ENCPART_ONLY
) {
213 code
= decode_EncryptedData(ticket
, ticket_len
, &t5
.enc_part
, &siz
);
219 code
= decode_Ticket(ticket
, ticket_len
, &t5
, &siz
);
223 serv_kvno
= t5
.tkt_vno
;
226 /* Check that the key type really fit into 8 bytes */
227 switch (t5
.enc_part
.etype
) {
228 case ETYPE_DES_CBC_CRC
:
229 case ETYPE_DES_CBC_MD4
:
230 case ETYPE_DES_CBC_MD5
:
231 key
.keytype
= t5
.enc_part
.etype
;
232 key
.keyvalue
.length
= 8;
233 key
.keyvalue
.data
= serv_key
.data
;
239 /* Get the service key. We have to assume that the key type is of
240 * size 8 bytes or else we can't store service keys for both krb4
241 * and krb5 in the same way in /usr/afs/etc/KeyFile.
243 code
= (*obj
->get_key
)(obj
->appl_data
, serv_kvno
, &serv_key
);
247 code
= krb5_crypto_init(context
, &key
, 0, &crypto
);
252 code
= krb5_decrypt(context
,
255 t5
.enc_part
.cipher
.data
,
256 t5
.enc_part
.cipher
.length
,
258 krb5_crypto_destroy(context
, crypto
);
263 code
= decode_EncTicketPart(plain
.data
, plain
.length
, &decr_part
, &siz
);
267 /* Check that the key type really fit into 8 bytes */
268 switch (decr_part
.key
.keytype
) {
269 case ETYPE_DES_CBC_CRC
:
270 case ETYPE_DES_CBC_MD4
:
271 case ETYPE_DES_CBC_MD5
:
278 principal
= calloc(1, sizeof(*principal
));
279 if (principal
== NULL
) {
283 code
= copy_PrincipalName(&decr_part
.cname
, &principal
->name
);
286 principal
->realm
= strdup(decr_part
.crealm
);
287 if (principal
->realm
== NULL
) {
293 /* Extract realm and principal */
294 code
= krb5_unparse_name(context
, principal
, user
);
295 krb5_free_principal(context
, principal
);
299 /* Extract session key */
300 memcpy(session_key
->data
, decr_part
.key
.keyvalue
.data
, 8);
302 /* Check lifetimes and host addresses, flags etc */
304 time_t now
= time(0); /* Use fast time package instead??? */
305 time_t start
= decr_part
.authtime
;
306 time_t end
= decr_part
.endtime
;
308 if (decr_part
.starttime
)
309 start
= *decr_part
.starttime
;
312 || start
> now
+ KTC_TIME_UNCERTAINTY
313 || decr_part
.flags
.invalid
)
316 if (now
> end
+ KTC_TIME_UNCERTAINTY
)
325 /* Check host addresses */
334 free_EncTicketPart(&decr_part
);
335 krb5_free_context(context
);
336 krb5_data_free(&plain
);
340 code
= RXKADUNKNOWNKEY
;
349 code
= RXKADBADTICKET
;
351 #endif /* HAVE_KRB5 */
356 decode_krb4_ticket(rxkad_serv_class
*obj
,
361 struct ktc_encryptionKey
*session_key
,
365 #if !defined(HAVE_KRB4) && !defined(HAVE_KRB5)
366 return RXKADBADTICKET
;
372 char sname
[SNAME_SZ
], sinstance
[INST_SZ
];
373 char name
[ANAME_SZ
], instance
[INST_SZ
], realm
[REALM_SZ
];
375 struct ktc_encryptionKey serv_key
; /* Service's secret key */
377 DES_key_schedule serv_sched
; /* Service's schedule */
379 /* First get service key */
380 int code
= (*obj
->get_key
)(obj
->appl_data
, serv_kvno
, &serv_key
);
382 return RXKADUNKNOWNKEY
;
384 memcpy(&key
, serv_key
.data
, sizeof(key
));
385 DES_key_sched(&key
, &serv_sched
);
386 tkt
.length
= ticket_len
;
387 memcpy(tkt
.dat
, ticket
, ticket_len
);
388 code
= decomp_ticket(&tkt
, &kflags
,
389 name
, instance
, realm
, &paddress
,
390 (void*)session_key
->data
, &klife
, &start
,
392 (DES_cblock
*)&serv_key
, &serv_sched
);
393 if (code
!= KSUCCESS
)
394 return RXKADBADTICKET
;
397 if (paddress
!= ntohl(con
->peer
->host
))
398 return RXKADBADTICKET
;
403 time_t end
= krb4_life_to_time(start
, klife
);
405 time_t end
= krb_life_to_time(start
, klife
);
407 time_t now
= time(0);
408 if (start
> CLOCK_SKEW
) /* Transarc sends 0 as start if localauth */
416 asprintf(user
, "%s%s%s@%s",
418 instance
[0] ? "." : "",
419 instance
[0] ? instance
: "",
423 return 0; /* Success */
424 #endif /* HAVE_KRB4 */
428 * Process a response to a challange.
432 server_CheckResponse(struct rx_securityClass
*obj_
,
433 struct rx_connection
*con
,
434 struct rx_packet
*pkt
)
436 rxkad_serv_class
*obj
= (rxkad_serv_class
*) obj_
;
437 serv_con_data
*cdat
= (serv_con_data
*) con
->securityData
;
439 int serv_kvno
; /* Service's kvno we used */
441 char ticket
[MAXKRB5TICKETLEN
];
447 if (rx_SlowReadPacket(pkt
, 0, sizeof(r
), &r
) != sizeof(r
))
448 return RXKADPACKETSHORT
;
450 serv_kvno
= ntohl(r
.kvno
);
451 ticket_len
= ntohl(r
.ticket_len
);
453 if (ticket_len
> MAXKRB5TICKETLEN
)
454 return RXKADTICKETLEN
;
456 if (rx_SlowReadPacket(pkt
, sizeof(r
), ticket_len
, ticket
) != ticket_len
)
457 return RXKADPACKETSHORT
;
459 /* Disassemble kerberos ticket */
460 if (serv_kvno
== RXKAD_TKT_TYPE_KERBEROS_V5
||
461 serv_kvno
== RXKAD_TKT_TYPE_KERBEROS_V5_ENCPART_ONLY
)
462 code
= decode_krb5_ticket(obj
, serv_kvno
, ticket
, ticket_len
,
463 &cdat
->k
.key
, &cdat
->expires
, &user
);
465 code
= decode_krb4_ticket(obj
, serv_kvno
, ticket
, ticket_len
,
466 &cdat
->k
.key
, &cdat
->expires
, &user
);
470 fc_keysched(cdat
->k
.key
.data
, cdat
->k
.keysched
);
472 /* Unseal r.encrypted */
473 fc_cbc_enc2(&r
.encrypted
, &r
.encrypted
, sizeof(r
.encrypted
),
474 cdat
->k
.keysched
, (uint32_t*)cdat
->k
.key
.data
, FC_DECRYPT
);
476 /* Verify response integrity */
477 cksum
= r
.encrypted
.cksum
;
478 r
.encrypted
.cksum
= 0;
479 if (r
.encrypted
.epoch
!= ntohl(con
->epoch
)
480 || r
.encrypted
.cid
!= ntohl(con
->cid
& RX_CIDMASK
)
481 || r
.encrypted
.security_index
!= ntohl(con
->securityIndex
)
482 || cksum
!= rxkad_cksum_response(&r
)) {
484 return RXKADSEALEDINCON
;
488 for (i
= 0; i
< RX_MAXCALLS
; i
++)
490 r
.encrypted
.call_numbers
[i
] = ntohl(r
.encrypted
.call_numbers
[i
]);
491 if (r
.encrypted
.call_numbers
[i
] < 0) {
493 return RXKADSEALEDINCON
;
498 if (ntohl(r
.encrypted
.inc_nonce
) != cdat
->nonce
+1) {
500 return RXKADOUTOFSEQUENCE
;
504 int level
= ntohl(r
.encrypted
.level
);
505 if ((level
< cdat
->cur_level
) || (level
> rxkad_crypt
)) {
507 return RXKADLEVELFAIL
;
509 cdat
->cur_level
= level
;
510 /* We don't use trailers but the transarc implementation breaks if
511 * we don't set the trailer size, packets get to large */
512 if (level
== rxkad_auth
)
514 rx_SetSecurityHeaderSize(con
, 4);
515 rx_SetSecurityMaxTrailerSize(con
, 4);
517 else if (level
== rxkad_crypt
)
519 rx_SetSecurityHeaderSize(con
, 8);
520 rx_SetSecurityMaxTrailerSize(con
, 8);
524 rxi_SetCallNumberVector(con
, r
.encrypted
.call_numbers
);
526 rxkad_calc_header_iv(con
, cdat
->k
.keysched
,
527 (void *)&cdat
->k
.key
, cdat
->e
.header_iv
);
528 cdat
->authenticated
= 1;
533 code
= obj
->user_ok(user
, serv_kvno
);
542 * Checksum and/or encrypt packet
546 server_PreparePacket(struct rx_securityClass
*obj_
,
547 struct rx_call
*call
,
548 struct rx_packet
*pkt
)
550 struct rx_connection
*con
= rx_ConnectionOf(call
);
551 serv_con_data
*cdat
= (serv_con_data
*) con
->securityData
;
552 key_stuff
*k
= &cdat
->k
;
553 end_stuff
*e
= &cdat
->e
;
555 return rxkad_prepare_packet(pkt
, con
, cdat
->cur_level
, k
, e
);
559 * Verify checksum and/or decrypt packet.
563 server_CheckPacket(struct rx_securityClass
*obj_
,
564 struct rx_call
*call
,
565 struct rx_packet
*pkt
)
567 struct rx_connection
*con
= rx_ConnectionOf(call
);
568 serv_con_data
*cdat
= (serv_con_data
*) con
->securityData
;
569 key_stuff
*k
= &cdat
->k
;
570 end_stuff
*e
= &cdat
->e
;
572 if (time(0) > cdat
->expires
) /* Use fast time package instead??? */
575 return rxkad_check_packet(pkt
, con
, cdat
->cur_level
, k
, e
);
580 server_GetStats(const struct rx_securityClass
*obj_
,
581 const struct rx_connection
*con
,
582 struct rx_securityObjectStats
*st
)
584 rxkad_serv_class
*obj
= (rxkad_serv_class
*) obj_
;
585 serv_con_data
*cdat
= (serv_con_data
*) con
->securityData
;
587 st
->type
= rxkad_disipline
;
588 st
->level
= obj
->min_level
;
589 st
->flags
= rxkad_checksummed
;
591 st
->flags
|= rxkad_unallocated
;
593 st
->bytesReceived
= cdat
->e
.bytesReceived
;
594 st
->packetsReceived
= cdat
->e
.packetsReceived
;
595 st
->bytesSent
= cdat
->e
.bytesSent
;
596 st
->packetsSent
= cdat
->e
.packetsSent
;
597 st
->expires
= cdat
->expires
;
598 st
->level
= cdat
->cur_level
;
599 if (cdat
->authenticated
)
600 st
->flags
|= rxkad_authenticated
;
605 static struct rx_securityOps server_ops
= {
607 server_NewConnection
,
608 server_PreparePacket
,
610 server_CheckAuthentication
,
611 server_CreateChallenge
,
614 server_CheckResponse
,
616 server_DestroyConnection
,
620 struct rx_securityClass
*
621 rxkad_NewServerSecurityObjectNew(/*rxkad_level*/ int min_level
,
623 int (*get_key
)(void *appl_data
,
625 struct ktc_encryptionKey
*key
),
626 int (*user_ok
)(char *name
,
629 rxkad_serv_class
*obj
;
634 obj
= (rxkad_serv_class
*) osi_Alloc(sizeof(rxkad_serv_class
));
635 obj
->klass
.refCount
= 1;
636 obj
->klass
.ops
= &server_ops
;
637 obj
->klass
.privateData
= (char *) obj
;
639 obj
->min_level
= min_level
;
640 obj
->appl_data
= appl_data
;
641 obj
->get_key
= get_key
;
642 obj
->user_ok
= user_ok
;