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>
26 #include <netinet/in.h>
38 #include <afs/afsutil.h>
39 #include <des/stats.h>
40 #include "private_data.h"
41 #define XPRT_RXKAD_SERVER
44 * This can be set to allow alternate ticket decoding.
45 * Currently only used by the AFS/DFS protocol translator to recognize
46 * Kerberos V5 tickets. The actual code to do that is provided externally.
48 afs_int32(*rxkad_AlternateTicketDecoder) ();
50 static struct rx_securityOps rxkad_server_ops = {
53 rxkad_PreparePacket, /* once per packet creation */
54 0, /* send packet (once per retrans) */
55 rxkad_CheckAuthentication,
56 rxkad_CreateChallenge,
60 rxkad_CheckPacket, /* check data packet */
61 rxkad_DestroyConnection,
67 extern afs_uint32 rx_MyMaxSendSize;
69 /* Miscellaneous random number routines that use the fcrypt module and the
72 static fc_KeySchedule random_int32_schedule;
74 #ifdef AFS_PTHREAD_ENV
76 * This mutex protects the following global variables:
77 * random_int32_schedule
82 pthread_mutex_t rxkad_random_mutex;
83 #define LOCK_RM assert(pthread_mutex_lock(&rxkad_random_mutex)==0)
84 #define UNLOCK_RM assert(pthread_mutex_unlock(&rxkad_random_mutex)==0)
88 #endif /* AFS_PTHREAD_ENV */
91 init_random_int32(void)
95 gettimeofday(&key, NULL);
97 fc_keysched((struct ktc_encryptionKey*)&key, random_int32_schedule);
102 get_random_int32(void)
104 static struct timeval seed;
108 fc_ecb_encrypt(&seed, &seed, random_int32_schedule, ENCRYPT);
114 /* Called with four parameters. The first is the level of encryption, as
115 defined in the rxkad.h file. The second and third are a rock and a
116 procedure that is called with the key version number that accompanies the
117 ticket and returns a pointer to the server's decryption key. The fourth
118 argument, if not NULL, is a pointer to a function that will be called for
119 every new connection with the name, instance and cell of the client. The
120 routine should return zero if the user is NOT acceptible to the server. If
121 this routine is not supplied, the server can call rxkad_GetServerInfo with
122 the rx connection pointer passed to the RPC routine to obtain information
126 rxkad_level level; * minimum level *
127 char *get_key_rock; * rock for get_key implementor *
128 int (*get_key)(); * passed kvno & addr(key) to fill *
129 int (*user_ok)(); * passed name, inst, cell => bool *
132 struct rx_securityClass *
133 rxkad_NewServerSecurityObject(rxkad_level level, char *get_key_rock,
134 int (*get_key) (char *get_key_rock, int kvno,
135 struct ktc_encryptionKey *
137 int (*user_ok) (char *name, char *instance,
138 char *cell, afs_int32 kvno))
140 struct rx_securityClass *tsc;
141 struct rxkad_sprivate *tsp;
147 size = sizeof(struct rx_securityClass);
148 tsc = (struct rx_securityClass *)osi_Alloc(size);
149 memset(tsc, 0, size);
150 tsc->refCount = 1; /* caller has one reference */
151 tsc->ops = &rxkad_server_ops;
152 size = sizeof(struct rxkad_sprivate);
153 tsp = (struct rxkad_sprivate *)osi_Alloc(size);
154 memset(tsp, 0, size);
155 tsc->privateData = (char *)tsp;
157 tsp->type |= rxkad_server; /* so can identify later */
158 tsp->level = level; /* level of encryption */
159 tsp->get_key_rock = get_key_rock;
160 tsp->get_key = get_key; /* to get server ticket */
161 tsp->user_ok = user_ok; /* to inform server of client id. */
164 INC_RXKAD_STATS(serverObjects);
168 /* server: called to tell if a connection authenticated properly */
171 rxkad_CheckAuthentication(struct rx_securityClass *aobj,
172 struct rx_connection *aconn)
174 struct rxkad_sconn *sconn;
176 /* first make sure the object exists */
177 if (!aconn->securityData)
178 return RXKADINCONSISTENCY;
180 sconn = (struct rxkad_sconn *)aconn->securityData;
181 return !sconn->authenticated;
184 /* server: put the current challenge in the connection structure for later use
188 rxkad_CreateChallenge(struct rx_securityClass *aobj,
189 struct rx_connection *aconn)
191 struct rxkad_sconn *sconn;
192 struct rxkad_sprivate *tsp;
194 sconn = (struct rxkad_sconn *)aconn->securityData;
195 sconn->challengeID = get_random_int32();
196 sconn->authenticated = 0; /* conn unauth. 'til we hear back */
197 /* initialize level from object's minimum acceptable level */
198 tsp = (struct rxkad_sprivate *)aobj->privateData;
199 sconn->level = tsp->level;
203 /* server: fill in a challenge in the packet */
206 rxkad_GetChallenge(struct rx_securityClass *aobj, struct rx_connection *aconn,
207 struct rx_packet *apacket)
209 struct rxkad_sconn *sconn;
212 struct rxkad_v2Challenge c_v2; /* version 2 */
213 struct rxkad_oldChallenge c_old; /* old style */
215 sconn = (struct rxkad_sconn *)aconn->securityData;
216 if (rx_IsUsingPktCksum(aconn))
217 sconn->cksumSeen = 1;
219 if (sconn->cksumSeen) {
220 memset(&c_v2, 0, sizeof(c_v2));
221 c_v2.version = htonl(RXKAD_CHALLENGE_PROTOCOL_VERSION);
222 c_v2.challengeID = htonl(sconn->challengeID);
223 c_v2.level = htonl((afs_int32) sconn->level);
225 challenge = (char *)&c_v2;
226 challengeSize = sizeof(c_v2);
228 memset(&c_old, 0, sizeof(c_old));
229 c_old.challengeID = htonl(sconn->challengeID);
230 c_old.level = htonl((afs_int32) sconn->level);
231 challenge = (char *)&c_old;
232 challengeSize = sizeof(c_old);
234 if (rx_MyMaxSendSize < challengeSize)
235 return RXKADPACKETSHORT; /* not enough space */
237 rx_packetwrite(apacket, 0, challengeSize, challenge);
238 rx_SetDataSize(apacket, challengeSize);
240 INC_RXKAD_STATS(challengesSent);
244 /* server: process a response to a challenge packet */
245 /* XXX this does some copying of data in and out of the packet, but I'll bet it
246 * could just do it in place, especially if I used rx_Pullup...
249 rxkad_CheckResponse(struct rx_securityClass *aobj,
250 struct rx_connection *aconn, struct rx_packet *apacket)
252 struct rxkad_sconn *sconn;
253 struct rxkad_sprivate *tsp;
254 struct ktc_encryptionKey serverKey;
255 struct rxkad_oldChallengeResponse oldr; /* response format */
256 struct rxkad_v2ChallengeResponse v2r;
257 afs_int32 tlen; /* ticket len */
258 afs_int32 kvno; /* key version of ticket */
259 char tix[MAXKTCTICKETLEN];
260 afs_int32 incChallengeID;
263 /* ticket contents */
264 struct ktc_principal client;
265 struct ktc_encryptionKey sessionkey;
270 struct rxkad_serverinfo *rock;
272 sconn = (struct rxkad_sconn *)aconn->securityData;
273 tsp = (struct rxkad_sprivate *)aobj->privateData;
275 if (sconn->cksumSeen) {
276 /* expect v2 response, leave fields in v2r in network order for cksum
277 * computation which follows decryption. */
278 if (rx_GetDataSize(apacket) < sizeof(v2r))
279 return RXKADPACKETSHORT;
280 rx_packetread(apacket, 0, sizeof(v2r), &v2r);
284 kvno = ntohl(v2r.kvno);
285 tlen = ntohl(v2r.ticketLen);
286 if (rx_GetDataSize(apacket) < sizeof(v2r) + tlen)
287 return RXKADPACKETSHORT;
289 /* expect old format response */
290 if (rx_GetDataSize(apacket) < sizeof(oldr))
291 return RXKADPACKETSHORT;
292 rx_packetread(apacket, 0, sizeof(oldr), &oldr);
295 kvno = ntohl(oldr.kvno);
296 tlen = ntohl(oldr.ticketLen);
297 if (rx_GetDataSize(apacket) != sizeof(oldr) + tlen)
298 return RXKADPACKETSHORT;
300 if ((tlen < MINKTCTICKETLEN) || (tlen > MAXKTCTICKETLEN))
301 return RXKADTICKETLEN;
303 rx_packetread(apacket, pos, tlen, tix); /* get ticket */
306 * We allow the ticket to be optionally decoded by an alternate
307 * ticket decoder, if the function variable
308 * rxkad_AlternateTicketDecoder is set. That function should
309 * return a code of -1 if it wants the ticket to be decoded by
310 * the standard decoder.
312 if (rxkad_AlternateTicketDecoder) {
314 rxkad_AlternateTicketDecoder(kvno, tix, tlen, client.name,
315 client.instance, client.cell,
316 &sessionkey, &host, &start, &end);
317 if (code && code != -1) {
321 code = -1; /* No alternate ticket decoder present */
325 * If the alternate decoder is not present, or returns -1, then
326 * assume the ticket is of the default style.
328 if (code == -1 && ((kvno == RXKAD_TKT_TYPE_KERBEROS_V5)
329 || (kvno == RXKAD_TKT_TYPE_KERBEROS_V5_ENCPART_ONLY))) {
331 tkt_DecodeTicket5(tix, tlen, tsp->get_key, tsp->get_key_rock,
332 kvno, client.name, client.instance, client.cell,
333 &sessionkey, &host, &start, &end);
339 * If the alternate decoder/kerberos 5 decoder is not present, or
340 * returns -1, then assume the ticket is of the default style.
343 /* get ticket's key */
344 code = (*tsp->get_key) (tsp->get_key_rock, kvno, &serverKey);
346 return RXKADUNKNOWNKEY; /* invalid kvno */
348 tkt_DecodeTicket(tix, tlen, &serverKey, client.name,
349 client.instance, client.cell, &sessionkey, &host,
352 return RXKADBADTICKET;
354 code = tkt_CheckTimes(start, end, time(0));
360 code = fc_keysched(&sessionkey, sconn->keysched);
363 memcpy(sconn->ivec, &sessionkey, sizeof(sconn->ivec));
365 if (sconn->cksumSeen) {
366 /* using v2 response */
367 afs_uint32 cksum; /* observed cksum */
368 struct rxkad_endpoint endpoint; /* connections endpoint */
372 memcpy(xor, sconn->ivec, 2 * sizeof(afs_int32));
373 fc_cbc_encrypt(&v2r.encrypted, &v2r.encrypted, sizeof(v2r.encrypted),
374 sconn->keysched, xor, DECRYPT);
375 cksum = rxkad_CksumChallengeResponse(&v2r);
376 if (cksum != v2r.encrypted.endpoint.cksum)
377 return RXKADSEALEDINCON;
378 (void)rxkad_SetupEndpoint(aconn, &endpoint);
379 v2r.encrypted.endpoint.cksum = 0;
380 if (memcmp(&endpoint, &v2r.encrypted.endpoint, sizeof(endpoint)) != 0)
381 return RXKADSEALEDINCON;
382 for (i = 0; i < RX_MAXCALLS; i++) {
383 v2r.encrypted.callNumbers[i] =
384 ntohl(v2r.encrypted.callNumbers[i]);
385 if (v2r.encrypted.callNumbers[i] < 0)
386 return RXKADSEALEDINCON;
389 (void)rxi_SetCallNumberVector(aconn, v2r.encrypted.callNumbers);
390 incChallengeID = ntohl(v2r.encrypted.incChallengeID);
391 level = ntohl(v2r.encrypted.level);
393 /* expect old format response */
394 fc_ecb_encrypt(&oldr.encrypted, &oldr.encrypted, sconn->keysched,
396 incChallengeID = ntohl(oldr.encrypted.incChallengeID);
397 level = ntohl(oldr.encrypted.level);
399 if (incChallengeID != sconn->challengeID + 1)
400 return RXKADOUTOFSEQUENCE; /* replay attempt */
401 if ((level < sconn->level) || (level > rxkad_crypt))
402 return RXKADLEVELFAIL;
403 sconn->level = level;
404 rxkad_SetLevel(aconn, sconn->level);
405 INC_RXKAD_STATS(responses[rxkad_LevelIndex(sconn->level)]);
406 /* now compute endpoint-specific info used for computing 16 bit checksum */
407 rxkad_DeriveXORInfo(aconn, sconn->keysched, sconn->ivec, sconn->preSeq);
409 /* otherwise things are ok */
410 sconn->expirationTime = end;
411 sconn->authenticated = 1;
414 code = tsp->user_ok(client.name, client.instance, client.cell, kvno);
417 } else { /* save the info for later retreival */
418 int size = sizeof(struct rxkad_serverinfo);
419 rock = (struct rxkad_serverinfo *)osi_Alloc(size);
420 memset(rock, 0, size);
422 memcpy(&rock->client, &client, sizeof(rock->client));
428 /* return useful authentication info about a server-side connection */
431 rxkad_GetServerInfo(struct rx_connection * aconn, rxkad_level * level,
432 afs_uint32 * expiration, char *name, char *instance,
433 char *cell, afs_int32 * kvno)
435 struct rxkad_sconn *sconn;
437 sconn = (struct rxkad_sconn *)aconn->securityData;
438 if (sconn && sconn->authenticated && sconn->rock
439 && (time(0) < sconn->expirationTime)) {
441 *level = sconn->level;
443 *expiration = sconn->expirationTime;
445 strcpy(name, sconn->rock->client.name);
447 strcpy(instance, sconn->rock->client.instance);
449 strcpy(cell, sconn->rock->client.cell);
451 *kvno = sconn->rock->kvno;