2 * Copyright 2000, International Business Machines Corporation and others.
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
10 /* The rxkad security object. Authentication using a DES-encrypted
11 * Kerberos-style ticket. These are the server-only routines. */
14 #include <afsconfig.h>
15 #include <afs/param.h>
21 #include <sys/types.h>
22 #if (defined(AFS_AIX_ENV) && defined(KERNEL) && !defined(UKERNEL)) || defined(AFS_AUX_ENV) || defined(AFS_SUN5_ENV)
23 #include <sys/systm.h>
29 #include <netinet/in.h>
41 #include <afs/afsutil.h>
42 #include <des/stats.h>
43 #include "private_data.h"
44 #define XPRT_RXKAD_SERVER
47 * This can be set to allow alternate ticket decoding.
48 * Currently only used by the AFS/DFS protocol translator to recognize
49 * Kerberos V5 tickets. The actual code to do that is provided externally.
51 afs_int32(*rxkad_AlternateTicketDecoder) ();
53 static struct rx_securityOps rxkad_server_ops = {
56 rxkad_PreparePacket, /* once per packet creation */
57 0, /* send packet (once per retrans) */
58 rxkad_CheckAuthentication,
59 rxkad_CreateChallenge,
63 rxkad_CheckPacket, /* check data packet */
64 rxkad_DestroyConnection,
70 extern afs_uint32 rx_MyMaxSendSize;
72 /* Miscellaneous random number routines that use the fcrypt module and the
75 static fc_KeySchedule random_int32_schedule;
77 #ifdef AFS_PTHREAD_ENV
79 * This mutex protects the following global variables:
80 * random_int32_schedule
85 pthread_mutex_t rxkad_random_mutex;
86 #define LOCK_RM assert(pthread_mutex_lock(&rxkad_random_mutex)==0)
87 #define UNLOCK_RM assert(pthread_mutex_unlock(&rxkad_random_mutex)==0)
91 #endif /* AFS_PTHREAD_ENV */
94 init_random_int32(void)
98 gettimeofday(&key, NULL);
100 fc_keysched((struct ktc_encryptionKey*)&key, random_int32_schedule);
105 get_random_int32(void)
107 static struct timeval seed;
111 fc_ecb_encrypt(&seed, &seed, random_int32_schedule, ENCRYPT);
117 /* Called with four parameters. The first is the level of encryption, as
118 defined in the rxkad.h file. The second and third are a rock and a
119 procedure that is called with the key version number that accompanies the
120 ticket and returns a pointer to the server's decryption key. The fourth
121 argument, if not NULL, is a pointer to a function that will be called for
122 every new connection with the name, instance and cell of the client. The
123 routine should return zero if the user is NOT acceptible to the server. If
124 this routine is not supplied, the server can call rxkad_GetServerInfo with
125 the rx connection pointer passed to the RPC routine to obtain information
129 rxkad_level level; * minimum level *
130 char *get_key_rock; * rock for get_key implementor *
131 int (*get_key)(); * passed kvno & addr(key) to fill *
132 int (*user_ok)(); * passed name, inst, cell => bool *
135 struct rx_securityClass *
136 rxkad_NewServerSecurityObject(rxkad_level level, char *get_key_rock,
137 int (*get_key) (char *get_key_rock, int kvno,
138 struct ktc_encryptionKey *
140 int (*user_ok) (char *name, char *instance,
141 char *cell, afs_int32 kvno))
143 struct rx_securityClass *tsc;
144 struct rxkad_sprivate *tsp;
150 size = sizeof(struct rx_securityClass);
151 tsc = (struct rx_securityClass *)osi_Alloc(size);
152 memset(tsc, 0, size);
153 tsc->refCount = 1; /* caller has one reference */
154 tsc->ops = &rxkad_server_ops;
155 size = sizeof(struct rxkad_sprivate);
156 tsp = (struct rxkad_sprivate *)osi_Alloc(size);
157 memset(tsp, 0, size);
158 tsc->privateData = (char *)tsp;
160 tsp->type |= rxkad_server; /* so can identify later */
161 tsp->level = level; /* level of encryption */
162 tsp->get_key_rock = get_key_rock;
163 tsp->get_key = get_key; /* to get server ticket */
164 tsp->user_ok = user_ok; /* to inform server of client id. */
167 INC_RXKAD_STATS(serverObjects);
171 /* server: called to tell if a connection authenticated properly */
174 rxkad_CheckAuthentication(struct rx_securityClass *aobj,
175 struct rx_connection *aconn)
177 struct rxkad_sconn *sconn;
179 /* first make sure the object exists */
180 if (!aconn->securityData)
181 return RXKADINCONSISTENCY;
183 sconn = (struct rxkad_sconn *)aconn->securityData;
184 return !sconn->authenticated;
187 /* server: put the current challenge in the connection structure for later use
191 rxkad_CreateChallenge(struct rx_securityClass *aobj,
192 struct rx_connection *aconn)
194 struct rxkad_sconn *sconn;
195 struct rxkad_sprivate *tsp;
197 sconn = (struct rxkad_sconn *)aconn->securityData;
198 sconn->challengeID = get_random_int32();
199 sconn->authenticated = 0; /* conn unauth. 'til we hear back */
200 /* initialize level from object's minimum acceptable level */
201 tsp = (struct rxkad_sprivate *)aobj->privateData;
202 sconn->level = tsp->level;
206 /* server: fill in a challenge in the packet */
209 rxkad_GetChallenge(struct rx_securityClass *aobj, struct rx_connection *aconn,
210 struct rx_packet *apacket)
212 struct rxkad_sconn *sconn;
215 struct rxkad_v2Challenge c_v2; /* version 2 */
216 struct rxkad_oldChallenge c_old; /* old style */
218 sconn = (struct rxkad_sconn *)aconn->securityData;
219 if (rx_IsUsingPktCksum(aconn))
220 sconn->cksumSeen = 1;
222 if (sconn->cksumSeen) {
223 memset(&c_v2, 0, sizeof(c_v2));
224 c_v2.version = htonl(RXKAD_CHALLENGE_PROTOCOL_VERSION);
225 c_v2.challengeID = htonl(sconn->challengeID);
226 c_v2.level = htonl((afs_int32) sconn->level);
228 challenge = (char *)&c_v2;
229 challengeSize = sizeof(c_v2);
231 memset(&c_old, 0, sizeof(c_old));
232 c_old.challengeID = htonl(sconn->challengeID);
233 c_old.level = htonl((afs_int32) sconn->level);
234 challenge = (char *)&c_old;
235 challengeSize = sizeof(c_old);
237 if (rx_MyMaxSendSize < challengeSize)
238 return RXKADPACKETSHORT; /* not enough space */
240 rx_packetwrite(apacket, 0, challengeSize, challenge);
241 rx_SetDataSize(apacket, challengeSize);
243 INC_RXKAD_STATS(challengesSent);
247 /* server: process a response to a challenge packet */
248 /* XXX this does some copying of data in and out of the packet, but I'll bet it
249 * could just do it in place, especially if I used rx_Pullup...
252 rxkad_CheckResponse(struct rx_securityClass *aobj,
253 struct rx_connection *aconn, struct rx_packet *apacket)
255 struct rxkad_sconn *sconn;
256 struct rxkad_sprivate *tsp;
257 struct ktc_encryptionKey serverKey;
258 struct rxkad_oldChallengeResponse oldr; /* response format */
259 struct rxkad_v2ChallengeResponse v2r;
260 afs_int32 tlen; /* ticket len */
261 afs_int32 kvno; /* key version of ticket */
262 char tix[MAXKTCTICKETLEN];
263 afs_int32 incChallengeID;
266 /* ticket contents */
267 struct ktc_principal client;
268 struct ktc_encryptionKey sessionkey;
273 struct rxkad_serverinfo *rock;
275 sconn = (struct rxkad_sconn *)aconn->securityData;
276 tsp = (struct rxkad_sprivate *)aobj->privateData;
278 if (sconn->cksumSeen) {
279 /* expect v2 response, leave fields in v2r in network order for cksum
280 * computation which follows decryption. */
281 if (rx_GetDataSize(apacket) < sizeof(v2r))
282 return RXKADPACKETSHORT;
283 rx_packetread(apacket, 0, sizeof(v2r), &v2r);
287 kvno = ntohl(v2r.kvno);
288 tlen = ntohl(v2r.ticketLen);
289 if (rx_GetDataSize(apacket) < sizeof(v2r) + tlen)
290 return RXKADPACKETSHORT;
292 /* expect old format response */
293 if (rx_GetDataSize(apacket) < sizeof(oldr))
294 return RXKADPACKETSHORT;
295 rx_packetread(apacket, 0, sizeof(oldr), &oldr);
298 kvno = ntohl(oldr.kvno);
299 tlen = ntohl(oldr.ticketLen);
300 if (rx_GetDataSize(apacket) != sizeof(oldr) + tlen)
301 return RXKADPACKETSHORT;
303 if ((tlen < MINKTCTICKETLEN) || (tlen > MAXKTCTICKETLEN))
304 return RXKADTICKETLEN;
306 rx_packetread(apacket, pos, tlen, tix); /* get ticket */
309 * We allow the ticket to be optionally decoded by an alternate
310 * ticket decoder, if the function variable
311 * rxkad_AlternateTicketDecoder is set. That function should
312 * return a code of -1 if it wants the ticket to be decoded by
313 * the standard decoder.
315 if (rxkad_AlternateTicketDecoder) {
317 rxkad_AlternateTicketDecoder(kvno, tix, tlen, client.name,
318 client.instance, client.cell,
319 &sessionkey, &host, &start, &end);
320 if (code && code != -1) {
324 code = -1; /* No alternate ticket decoder present */
328 * If the alternate decoder is not present, or returns -1, then
329 * assume the ticket is of the default style.
331 if (code == -1 && ((kvno == RXKAD_TKT_TYPE_KERBEROS_V5)
332 || (kvno == RXKAD_TKT_TYPE_KERBEROS_V5_ENCPART_ONLY))) {
334 tkt_DecodeTicket5(tix, tlen, tsp->get_key, tsp->get_key_rock,
335 kvno, client.name, client.instance, client.cell,
336 &sessionkey, &host, &start, &end);
342 * If the alternate decoder/kerberos 5 decoder is not present, or
343 * returns -1, then assume the ticket is of the default style.
346 /* get ticket's key */
347 code = (*tsp->get_key) (tsp->get_key_rock, kvno, &serverKey);
349 return RXKADUNKNOWNKEY; /* invalid kvno */
351 tkt_DecodeTicket(tix, tlen, &serverKey, client.name,
352 client.instance, client.cell, &sessionkey, &host,
357 code = tkt_CheckTimes(start, end, time(0));
363 return RXKADBADTICKET;
365 code = fc_keysched(&sessionkey, sconn->keysched);
368 memcpy(sconn->ivec, &sessionkey, sizeof(sconn->ivec));
370 if (sconn->cksumSeen) {
371 /* using v2 response */
372 afs_uint32 cksum; /* observed cksum */
373 struct rxkad_endpoint endpoint; /* connections endpoint */
377 memcpy(xor, sconn->ivec, 2 * sizeof(afs_int32));
378 fc_cbc_encrypt(&v2r.encrypted, &v2r.encrypted, sizeof(v2r.encrypted),
379 sconn->keysched, xor, DECRYPT);
380 cksum = rxkad_CksumChallengeResponse(&v2r);
381 if (cksum != v2r.encrypted.endpoint.cksum)
382 return RXKADSEALEDINCON;
383 (void)rxkad_SetupEndpoint(aconn, &endpoint);
384 v2r.encrypted.endpoint.cksum = 0;
385 if (memcmp(&endpoint, &v2r.encrypted.endpoint, sizeof(endpoint)) != 0)
386 return RXKADSEALEDINCON;
387 for (i = 0; i < RX_MAXCALLS; i++) {
388 v2r.encrypted.callNumbers[i] =
389 ntohl(v2r.encrypted.callNumbers[i]);
390 if (v2r.encrypted.callNumbers[i] < 0)
391 return RXKADSEALEDINCON;
394 (void)rxi_SetCallNumberVector(aconn, v2r.encrypted.callNumbers);
395 incChallengeID = ntohl(v2r.encrypted.incChallengeID);
396 level = ntohl(v2r.encrypted.level);
398 /* expect old format response */
399 fc_ecb_encrypt(&oldr.encrypted, &oldr.encrypted, sconn->keysched,
401 incChallengeID = ntohl(oldr.encrypted.incChallengeID);
402 level = ntohl(oldr.encrypted.level);
404 if (incChallengeID != sconn->challengeID + 1)
405 return RXKADOUTOFSEQUENCE; /* replay attempt */
406 if ((level < sconn->level) || (level > rxkad_crypt))
407 return RXKADLEVELFAIL;
408 sconn->level = level;
409 rxkad_SetLevel(aconn, sconn->level);
410 INC_RXKAD_STATS(responses[rxkad_LevelIndex(sconn->level)]);
411 /* now compute endpoint-specific info used for computing 16 bit checksum */
412 rxkad_DeriveXORInfo(aconn, sconn->keysched, sconn->ivec, sconn->preSeq);
414 /* otherwise things are ok */
415 sconn->expirationTime = end;
416 sconn->authenticated = 1;
419 code = tsp->user_ok(client.name, client.instance, client.cell, kvno);
422 } else { /* save the info for later retreival */
423 int size = sizeof(struct rxkad_serverinfo);
424 rock = (struct rxkad_serverinfo *)osi_Alloc(size);
425 memset(rock, 0, size);
427 memcpy(&rock->client, &client, sizeof(rock->client));
433 /* return useful authentication info about a server-side connection */
436 rxkad_GetServerInfo(struct rx_connection * aconn, rxkad_level * level,
437 afs_uint32 * expiration, char *name, char *instance,
438 char *cell, afs_int32 * kvno)
440 struct rxkad_sconn *sconn;
442 sconn = (struct rxkad_sconn *)aconn->securityData;
443 if (sconn && sconn->authenticated && sconn->rock
444 && (time(0) < sconn->expirationTime)) {
446 *level = sconn->level;
448 *expiration = sconn->expirationTime;
450 strcpy(name, sconn->rock->client.name);
452 strcpy(instance, sconn->rock->client.instance);
454 strcpy(cell, sconn->rock->client.cell);
456 *kvno = sconn->rock->kvno;