RX: Make rxi_Alloc return (void *)
[openafs.git] / src / rxkad / rxkad_client.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  *
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
8  */
9
10 /* The rxkad security object.  Authentication using a DES-encrypted
11  * Kerberos-style ticket.  These are the client-only routines.  They do not
12  * make any use of DES. */
13
14 #include <afsconfig.h>
15 #ifdef KERNEL
16 #include "afs/param.h"
17 #else
18 #include <afs/param.h>
19 #endif
20
21
22 #ifdef KERNEL
23 #include "afs/stds.h"
24 #ifndef UKERNEL
25 #include "h/types.h"
26 #include "h/time.h"
27 #if defined(AFS_AIX_ENV) || defined(AFS_AUX_ENV) || defined(AFS_SUN5_ENV) || defined(AFS_XBSD_ENV)
28 #include "h/systm.h"
29 #endif
30 #ifdef AFS_LINUX20_ENV
31 #include "h/socket.h"
32 #endif
33 #ifndef AFS_OBSD_ENV
34 #include "netinet/in.h"
35 #endif
36 #else /* !UKERNEL */
37 #include "afs/sysincludes.h"
38 #endif /* !UKERNEL */
39 #ifndef AFS_LINUX22_ENV
40 #include "rpc/types.h"
41 #include "rx/xdr.h"
42 #endif
43 #include "rx/rx.h"
44 #else /* ! KERNEL */
45 #include <afs/stds.h>
46 #include <sys/types.h>
47 #include <time.h>
48 #include <string.h>
49 #ifdef AFS_NT40_ENV
50 #include <winsock2.h>
51 #else
52 #include <netinet/in.h>
53 #include <unistd.h>
54 #endif
55 #include <rx/rx.h>
56 #include <rx/xdr.h>
57 #ifdef AFS_PTHREAD_ENV
58 #include "rx/rxkad.h"
59 #endif /* AFS_PTHREAD_ENV */
60 #endif /* KERNEL */
61
62 #include <des/stats.h>
63 #include "private_data.h"
64 #define XPRT_RXKAD_CLIENT
65
66 #ifndef max
67 #define max(a,b)    ((a) < (b)? (b) : (a))
68 #endif /* max */
69
70 static struct rx_securityOps rxkad_client_ops = {
71     rxkad_Close,
72     rxkad_NewConnection,        /* every new connection */
73     rxkad_PreparePacket,        /* once per packet creation */
74     0,                          /* send packet (once per retrans.) */
75     0,
76     0,
77     0,
78     rxkad_GetResponse,          /* respond to challenge packet */
79     0,
80     rxkad_CheckPacket,          /* check data packet */
81     rxkad_DestroyConnection,
82     rxkad_GetStats,
83     0,
84     0,
85     0,
86 };
87
88 /* To minimize changes to epoch, we set this Cuid once, and everyone (including
89  * rxnull) uses it after that.  This means that the Ksession of the first
90  * authencticated connection should be a good one. */
91
92 #ifdef AFS_PTHREAD_ENV
93 /*
94  * This mutex protects the following global variables:
95  * Cuid
96  * counter
97  * rxkad_EpochWasSet
98  */
99 #include <assert.h>
100 pthread_mutex_t rxkad_client_uid_mutex;
101 #define LOCK_CUID assert(pthread_mutex_lock(&rxkad_client_uid_mutex)==0)
102 #define UNLOCK_CUID assert(pthread_mutex_unlock(&rxkad_client_uid_mutex)==0)
103 #else
104 #define LOCK_CUID
105 #define UNLOCK_CUID
106 #endif /* AFS_PTHREAD_ENV */
107
108 static afs_int32 Cuid[2];       /* set once and shared by all */
109 int rxkad_EpochWasSet = 0;      /* TRUE => we called rx_SetEpoch */
110
111 /* allocate a new connetion ID in place */
112 int
113 rxkad_AllocCID(struct rx_securityClass *aobj, struct rx_connection *aconn)
114 {
115     struct rxkad_cprivate *tcp;
116     struct rxkad_cidgen tgen;
117     static afs_int32 counter = 0;       /* not used anymore */
118
119     LOCK_CUID;
120     if (Cuid[0] == 0) {
121         afs_uint32 xor[2];
122         tgen.ipAddr = rxi_getaddr();    /* comes back in net order */
123         clock_GetTime(&tgen.time);      /* changes time1 and time2 */
124         tgen.time.sec = htonl(tgen.time.sec);
125         tgen.time.usec = htonl(tgen.time.usec);
126         tgen.counter = htonl(counter);
127         counter++;
128 #ifdef KERNEL
129         tgen.random1 = afs_random() & 0x7fffffff;       /* was "80000" */
130         tgen.random2 = afs_random() & 0x7fffffff;       /* was "htonl(100)" */
131 #else
132         tgen.random1 = htonl(getpid());
133         tgen.random2 = htonl(100);
134 #endif
135         if (aobj) {
136             /* block is ready for encryption with session key, let's go for it. */
137             tcp = (struct rxkad_cprivate *)aobj->privateData;
138             memcpy((void *)xor, (void *)tcp->ivec, 2 * sizeof(afs_int32));
139             fc_cbc_encrypt((char *)&tgen, (char *)&tgen, sizeof(tgen),
140                            tcp->keysched, xor, ENCRYPT);
141         } else {
142             /* Create a session key so that we can encrypt it */
143
144         }
145         memcpy((void *)Cuid,
146                ((char *)&tgen) + sizeof(tgen) - ENCRYPTIONBLOCKSIZE,
147                ENCRYPTIONBLOCKSIZE);
148         Cuid[0] = (Cuid[0] & ~0x40000000) | 0x80000000;
149         Cuid[1] &= RX_CIDMASK;
150         rx_SetEpoch(Cuid[0]);   /* for future rxnull connections */
151         rxkad_EpochWasSet++;
152     }
153
154     if (!aconn) {
155         UNLOCK_CUID;
156         return 0;
157     }
158     aconn->epoch = Cuid[0];
159     aconn->cid = Cuid[1];
160     Cuid[1] += 1 << RX_CIDSHIFT;
161     UNLOCK_CUID;
162     return 0;
163 }
164
165 /* Allocate a new client security object.  Called with the encryption level,
166  * the session key and the ticket for the other side obtained from the
167  * AuthServer.  Refers to export control to determine level. */
168
169 struct rx_securityClass *
170 rxkad_NewClientSecurityObject(rxkad_level level,
171                               struct ktc_encryptionKey *sessionkey,
172                               afs_int32 kvno, int ticketLen, char *ticket)
173 {
174     struct rx_securityClass *tsc;
175     struct rxkad_cprivate *tcp;
176     int code;
177     int size, psize;
178
179     size = sizeof(struct rx_securityClass);
180     tsc = rxi_Alloc(size);
181     memset((void *)tsc, 0, size);
182     tsc->refCount = 1;          /* caller gets one for free */
183     tsc->ops = &rxkad_client_ops;
184
185     psize = PDATA_SIZE(ticketLen);
186     tcp = rxi_Alloc(psize);
187     memset((void *)tcp, 0, psize);
188     tsc->privateData = (char *)tcp;
189     tcp->type |= rxkad_client;
190     tcp->level = level;
191     code = fc_keysched(sessionkey, tcp->keysched);
192     if (code) {
193         rxi_Free(tcp, psize);
194         rxi_Free(tsc, sizeof(struct rx_securityClass));
195         return 0;               /* bad key */
196     }
197     memcpy((void *)tcp->ivec, (void *)sessionkey, sizeof(tcp->ivec));
198     tcp->kvno = kvno;           /* key version number */
199     tcp->ticketLen = ticketLen; /* length of ticket */
200     if (tcp->ticketLen > MAXKTCTICKETLEN) {
201         rxi_Free(tcp, psize);
202         rxi_Free(tsc, sizeof(struct rx_securityClass));
203         return 0;               /* bad key */
204     }
205     memcpy(tcp->ticket, ticket, ticketLen);
206
207     INC_RXKAD_STATS(clientObjects);
208     return tsc;
209 }
210
211 /* client: respond to a challenge packet */
212
213 int
214 rxkad_GetResponse(struct rx_securityClass *aobj, struct rx_connection *aconn,
215                   struct rx_packet *apacket)
216 {
217     struct rxkad_cprivate *tcp;
218     char *tp;
219     int v2;                     /* whether server is old style or v2 */
220     afs_int32 challengeID;
221     rxkad_level level;
222     char *response;
223     int responseSize, missing;
224     struct rxkad_v2ChallengeResponse r_v2;
225     struct rxkad_oldChallengeResponse r_old;
226
227     tcp = (struct rxkad_cprivate *)aobj->privateData;
228
229     if (!(tcp->type & rxkad_client))
230         return RXKADINCONSISTENCY;
231
232     v2 = (rx_Contiguous(apacket) > sizeof(struct rxkad_oldChallenge));
233     tp = rx_DataOf(apacket);
234
235     if (v2) {                   /* v2 challenge */
236         struct rxkad_v2Challenge *c_v2;
237         if (rx_GetDataSize(apacket) < sizeof(struct rxkad_v2Challenge))
238             return RXKADPACKETSHORT;
239         c_v2 = (struct rxkad_v2Challenge *)tp;
240         challengeID = ntohl(c_v2->challengeID);
241         level = ntohl(c_v2->level);
242     } else {                    /* old format challenge */
243         struct rxkad_oldChallenge *c_old;
244         if (rx_GetDataSize(apacket) < sizeof(struct rxkad_oldChallenge))
245             return RXKADPACKETSHORT;
246         c_old = (struct rxkad_oldChallenge *)tp;
247         challengeID = ntohl(c_old->challengeID);
248         level = ntohl(c_old->level);
249     }
250
251     if (level > tcp->level)
252         return RXKADLEVELFAIL;
253     INC_RXKAD_STATS(challenges[rxkad_LevelIndex(tcp->level)]);
254     if (v2) {
255         int i;
256         afs_uint32 xor[2];
257         memset((void *)&r_v2, 0, sizeof(r_v2));
258         r_v2.version = htonl(RXKAD_CHALLENGE_PROTOCOL_VERSION);
259         r_v2.spare = 0;
260         (void)rxkad_SetupEndpoint(aconn, &r_v2.encrypted.endpoint);
261         (void)rxi_GetCallNumberVector(aconn, r_v2.encrypted.callNumbers);
262         for (i = 0; i < RX_MAXCALLS; i++) {
263             if (r_v2.encrypted.callNumbers[i] < 0)
264                 return RXKADINCONSISTENCY;
265             r_v2.encrypted.callNumbers[i] =
266                 htonl(r_v2.encrypted.callNumbers[i]);
267         }
268         r_v2.encrypted.incChallengeID = htonl(challengeID + 1);
269         r_v2.encrypted.level = htonl((afs_int32) tcp->level);
270         r_v2.kvno = htonl(tcp->kvno);
271         r_v2.ticketLen = htonl(tcp->ticketLen);
272         r_v2.encrypted.endpoint.cksum = rxkad_CksumChallengeResponse(&r_v2);
273         memcpy((void *)xor, (void *)tcp->ivec, 2 * sizeof(afs_int32));
274         fc_cbc_encrypt(&r_v2.encrypted, &r_v2.encrypted,
275                        sizeof(r_v2.encrypted), tcp->keysched, xor, ENCRYPT);
276         response = (char *)&r_v2;
277         responseSize = sizeof(r_v2);
278     } else {
279         memset((void *)&r_old, 0, sizeof(r_old));
280         r_old.encrypted.incChallengeID = htonl(challengeID + 1);
281         r_old.encrypted.level = htonl((afs_int32) tcp->level);
282         r_old.kvno = htonl(tcp->kvno);
283         r_old.ticketLen = htonl(tcp->ticketLen);
284         fc_ecb_encrypt(&r_old.encrypted, &r_old.encrypted, tcp->keysched,
285                        ENCRYPT);
286         response = (char *)&r_old;
287         responseSize = sizeof(r_old);
288     }
289
290     if (RX_MAX_PACKET_DATA_SIZE < responseSize + tcp->ticketLen)
291         return RXKADPACKETSHORT;        /* not enough space */
292
293     rx_computelen(apacket, missing);
294     missing = responseSize + tcp->ticketLen - missing;
295     if (missing > 0)
296         if (rxi_AllocDataBuf(apacket, missing, RX_PACKET_CLASS_SEND) > 0)
297             return RXKADPACKETSHORT;    /* not enough space */
298
299     /* copy response and ticket into packet */
300     rx_packetwrite(apacket, 0, responseSize, response);
301     rx_packetwrite(apacket, responseSize, tcp->ticketLen, tcp->ticket);
302
303     rx_SetDataSize(apacket, responseSize + tcp->ticketLen);
304     return 0;
305 }
306
307 void
308 rxkad_ResetState(void)
309 {
310     LOCK_CUID;
311     Cuid[0] = 0;
312     rxkad_EpochWasSet = 0;
313     UNLOCK_CUID;
314 }