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