975f9bca499e37e4d1f2777534028b27fb3faf2e
[metze/old/v3-2-winbind-ndr.git] / source / libsmb / credentials.c
1 /* 
2    Unix SMB/CIFS implementation.
3    code to manipulate domain credentials
4    Copyright (C) Andrew Tridgell 1997-1998
5    Largely rewritten by Jeremy Allison 2005.
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #include "includes.h"
23
24 /****************************************************************************
25  Represent a credential as a string.
26 ****************************************************************************/
27
28 char *credstr(const unsigned char *cred)
29 {
30         static fstring buf;
31         slprintf(buf, sizeof(buf) - 1, "%02X%02X%02X%02X%02X%02X%02X%02X",
32                 cred[0], cred[1], cred[2], cred[3], 
33                 cred[4], cred[5], cred[6], cred[7]);
34         return buf;
35 }
36
37 /****************************************************************************
38  Setup the session key and the client and server creds in dc.
39  ADS-style 128 bit session keys.
40  Used by both client and server creds setup.
41 ****************************************************************************/
42
43 static void creds_init_128(struct dcinfo *dc,
44                                 const DOM_CHAL *clnt_chal_in,
45                                 const DOM_CHAL *srv_chal_in,
46                                 const char mach_pw[16])
47 {
48         unsigned char zero[4], tmp[16];
49         HMACMD5Context ctx;
50         struct MD5Context md5;
51
52         /* Just in case this isn't already there */
53         memcpy(dc->mach_pw, mach_pw, 16);
54
55         ZERO_STRUCT(dc->sess_key);
56
57         memset(zero, 0, sizeof(zero));
58
59         hmac_md5_init_rfc2104(mach_pw, 16, &ctx);
60         MD5Init(&md5);
61         MD5Update(&md5, zero, sizeof(zero));
62         MD5Update(&md5, clnt_chal_in->data, 8);
63         MD5Update(&md5, srv_chal_in->data, 8);
64         MD5Final(tmp, &md5);
65         hmac_md5_update(tmp, sizeof(tmp), &ctx);
66         hmac_md5_final(dc->sess_key, &ctx);
67
68         /* debug output */
69         DEBUG(5,("creds_init_128\n"));
70         DEBUG(5,("\tclnt_chal_in: %s\n", credstr(clnt_chal_in->data)));
71         DEBUG(5,("\tsrv_chal_in : %s\n", credstr(srv_chal_in->data)));
72         dump_data_pw("\tsession_key ", (const unsigned char *)dc->sess_key, 16);
73
74         /* Generate the next client and server creds. */
75         
76         des_crypt112(dc->clnt_chal.data,                /* output */
77                         clnt_chal_in->data,             /* input */
78                         dc->sess_key,                   /* input */
79                         1);
80
81         des_crypt112(dc->srv_chal.data,                 /* output */
82                         srv_chal_in->data,              /* input */
83                         dc->sess_key,                   /* input */
84                         1);
85
86         /* Seed is the client chal. */
87         memcpy(dc->seed_chal.data, dc->clnt_chal.data, 8);
88 }
89
90 /****************************************************************************
91  Setup the session key and the client and server creds in dc.
92  Used by both client and server creds setup.
93 ****************************************************************************/
94
95 static void creds_init_64(struct dcinfo *dc,
96                         const DOM_CHAL *clnt_chal_in,
97                         const DOM_CHAL *srv_chal_in,
98                         const char mach_pw[16])
99 {
100         uint32 sum[2];
101         unsigned char sum2[8];
102
103         /* Just in case this isn't already there */
104         memcpy(dc->mach_pw, mach_pw, 16);
105
106         sum[0] = IVAL(clnt_chal_in->data, 0) + IVAL(srv_chal_in->data, 0);
107         sum[1] = IVAL(clnt_chal_in->data, 4) + IVAL(srv_chal_in->data, 4);
108
109         SIVAL(sum2,0,sum[0]);
110         SIVAL(sum2,4,sum[1]);
111
112         ZERO_STRUCT(dc->sess_key);
113
114         des_crypt128(dc->sess_key, sum2, dc->mach_pw);
115
116         /* debug output */
117         DEBUG(5,("creds_init_64\n"));
118         DEBUG(5,("\tclnt_chal_in: %s\n", credstr(clnt_chal_in->data)));
119         DEBUG(5,("\tsrv_chal_in : %s\n", credstr(srv_chal_in->data)));
120         DEBUG(5,("\tclnt+srv : %s\n", credstr(sum2)));
121         DEBUG(5,("\tsess_key_out : %s\n", credstr(dc->sess_key)));
122
123         /* Generate the next client and server creds. */
124         
125         des_crypt112(dc->clnt_chal.data,                /* output */
126                         clnt_chal_in->data,             /* input */
127                         dc->sess_key,                   /* input */
128                         1);
129
130         des_crypt112(dc->srv_chal.data,                 /* output */
131                         srv_chal_in->data,              /* input */
132                         dc->sess_key,                   /* input */
133                         1);
134
135         /* Seed is the client chal. */
136         memcpy(dc->seed_chal.data, dc->clnt_chal.data, 8);
137 }
138
139 /****************************************************************************
140  Utility function to step credential chain one forward.
141  Deliberately doesn't update the seed. See reseed comment below.
142 ****************************************************************************/
143
144 static void creds_step(struct dcinfo *dc)
145 {
146         DOM_CHAL time_chal;
147
148         DEBUG(5,("\tsequence = 0x%x\n", (unsigned int)dc->sequence ));
149
150         DEBUG(5,("\tseed:        %s\n", credstr(dc->seed_chal.data) ));
151
152         SIVAL(time_chal.data, 0, IVAL(dc->seed_chal.data, 0) + dc->sequence);
153         SIVAL(time_chal.data, 4, IVAL(dc->seed_chal.data, 4));
154                                                                                                    
155         DEBUG(5,("\tseed+seq   %s\n", credstr(time_chal.data) ));
156
157         des_crypt112(dc->clnt_chal.data, time_chal.data, dc->sess_key, 1);
158
159         DEBUG(5,("\tCLIENT      %s\n", credstr(dc->clnt_chal.data) ));
160
161         SIVAL(time_chal.data, 0, IVAL(dc->seed_chal.data, 0) + dc->sequence + 1);
162         SIVAL(time_chal.data, 4, IVAL(dc->seed_chal.data, 4));
163
164         DEBUG(5,("\tseed+seq+1   %s\n", credstr(time_chal.data) ));
165
166         des_crypt112(dc->srv_chal.data, time_chal.data, dc->sess_key, 1);
167
168         DEBUG(5,("\tSERVER      %s\n", credstr(dc->srv_chal.data) ));
169 }
170
171 /****************************************************************************
172  Create a server credential struct.
173 ****************************************************************************/
174
175 void creds_server_init(uint32 neg_flags,
176                         struct dcinfo *dc,
177                         DOM_CHAL *clnt_chal,
178                         DOM_CHAL *srv_chal,
179                         const char mach_pw[16],
180                         DOM_CHAL *init_chal_out)
181 {
182         DEBUG(10,("creds_server_init: neg_flags : %x\n", (unsigned int)neg_flags));
183         DEBUG(10,("creds_server_init: client chal : %s\n", credstr(clnt_chal->data) ));
184         DEBUG(10,("creds_server_init: server chal : %s\n", credstr(srv_chal->data) ));
185         dump_data_pw("creds_server_init: machine pass", (const unsigned char *)mach_pw, 16);
186
187         /* Generate the session key and the next client and server creds. */
188         if (neg_flags & NETLOGON_NEG_128BIT) {
189                 creds_init_128(dc,
190                         clnt_chal,
191                         srv_chal,
192                         mach_pw);
193         } else {
194                 creds_init_64(dc,
195                         clnt_chal,
196                         srv_chal,
197                         mach_pw);
198         }
199
200         dump_data_pw("creds_server_init: session key", dc->sess_key, 16);
201
202         DEBUG(10,("creds_server_init: clnt : %s\n", credstr(dc->clnt_chal.data) ));
203         DEBUG(10,("creds_server_init: server : %s\n", credstr(dc->srv_chal.data) ));
204         DEBUG(10,("creds_server_init: seed : %s\n", credstr(dc->seed_chal.data) ));
205
206         memcpy(init_chal_out->data, dc->srv_chal.data, 8);
207 }
208
209 /****************************************************************************
210  Check a credential sent by the client.
211 ****************************************************************************/
212
213 BOOL creds_server_check(const struct dcinfo *dc, const DOM_CHAL *rcv_cli_chal_in)
214 {
215         if (memcmp(dc->clnt_chal.data, rcv_cli_chal_in->data, 8)) {
216                 DEBUG(5,("creds_server_check: challenge : %s\n", credstr(rcv_cli_chal_in->data)));
217                 DEBUG(5,("calculated: %s\n", credstr(dc->clnt_chal.data)));
218                 DEBUG(2,("creds_server_check: credentials check failed.\n"));
219                 return False;
220         }
221         DEBUG(10,("creds_server_check: credentials check OK.\n"));
222         return True;
223 }
224
225 /****************************************************************************
226  Replace current seed chal. Internal function - due to split server step below.
227 ****************************************************************************/
228
229 static void creds_reseed(struct dcinfo *dc)
230 {
231         DOM_CHAL time_chal;
232
233         SIVAL(time_chal.data, 0, IVAL(dc->seed_chal.data, 0) + dc->sequence + 1);
234         SIVAL(time_chal.data, 4, IVAL(dc->seed_chal.data, 4));
235
236         dc->seed_chal = time_chal;
237
238         DEBUG(5,("cred_reseed: seed %s\n", credstr(dc->seed_chal.data) ));
239 }
240
241 /****************************************************************************
242  Step the server credential chain one forward. 
243 ****************************************************************************/
244
245 BOOL creds_server_step(struct dcinfo *dc, const DOM_CRED *received_cred, DOM_CRED *cred_out)
246 {
247         BOOL ret;
248         struct dcinfo tmp_dc = *dc;
249
250         /* Do all operations on a temporary copy of the dc,
251            which we throw away if the checks fail. */
252
253         tmp_dc.sequence = received_cred->timestamp.time;
254
255         creds_step(&tmp_dc);
256
257         /* Create the outgoing credentials */
258         cred_out->timestamp.time = tmp_dc.sequence + 1;
259         cred_out->challenge = tmp_dc.srv_chal;
260
261         creds_reseed(&tmp_dc);
262
263         ret = creds_server_check(&tmp_dc, &received_cred->challenge);
264         if (!ret) {
265                 return False;
266         }
267
268         /* creds step succeeded - replace the current creds. */
269         *dc = tmp_dc;
270         return True;
271 }
272
273 /****************************************************************************
274  Create a client credential struct.
275 ****************************************************************************/
276
277 void creds_client_init(uint32 neg_flags,
278                         struct dcinfo *dc,
279                         DOM_CHAL *clnt_chal,
280                         DOM_CHAL *srv_chal,
281                         const unsigned char mach_pw[16],
282                         DOM_CHAL *init_chal_out)
283 {
284         dc->sequence = time(NULL);
285
286         DEBUG(10,("creds_client_init: neg_flags : %x\n", (unsigned int)neg_flags));
287         DEBUG(10,("creds_client_init: client chal : %s\n", credstr(clnt_chal->data) ));
288         DEBUG(10,("creds_client_init: server chal : %s\n", credstr(srv_chal->data) ));
289         dump_data_pw("creds_client_init: machine pass", (const unsigned char *)mach_pw, 16);
290
291         /* Generate the session key and the next client and server creds. */
292         if (neg_flags & NETLOGON_NEG_128BIT) {
293                 creds_init_128(dc,
294                                 clnt_chal,
295                                 srv_chal,
296                                 mach_pw);
297         } else {
298                 creds_init_64(dc,
299                         clnt_chal,
300                         srv_chal,
301                         mach_pw);
302         }
303
304         dump_data_pw("creds_client_init: session key", dc->sess_key, 16);
305
306         DEBUG(10,("creds_client_init: clnt : %s\n", credstr(dc->clnt_chal.data) ));
307         DEBUG(10,("creds_client_init: server : %s\n", credstr(dc->srv_chal.data) ));
308         DEBUG(10,("creds_client_init: seed : %s\n", credstr(dc->seed_chal.data) ));
309
310         memcpy(init_chal_out->data, dc->clnt_chal.data, 8);
311 }
312
313 /****************************************************************************
314  Check a credential returned by the server.
315 ****************************************************************************/
316
317 BOOL creds_client_check(const struct dcinfo *dc, const DOM_CHAL *rcv_srv_chal_in)
318 {
319         if (memcmp(dc->srv_chal.data, rcv_srv_chal_in->data, 8)) {
320                 DEBUG(5,("creds_client_check: challenge : %s\n", credstr(rcv_srv_chal_in->data)));
321                 DEBUG(5,("calculated: %s\n", credstr(dc->srv_chal.data)));
322                 DEBUG(0,("creds_client_check: credentials check failed.\n"));
323                 return False;
324         }
325         DEBUG(10,("creds_client_check: credentials check OK.\n"));
326         return True;
327 }
328
329 /****************************************************************************
330   Step the client credentials to the next element in the chain, updating the
331   current client and server credentials and the seed
332   produce the next authenticator in the sequence ready to send to
333   the server
334 ****************************************************************************/
335
336 void creds_client_step(struct dcinfo *dc, DOM_CRED *next_cred_out)
337 {
338         dc->sequence += 2;
339         creds_step(dc);
340         creds_reseed(dc);
341
342         next_cred_out->challenge = dc->clnt_chal;
343         next_cred_out->timestamp.time = dc->sequence;
344 }