Merge branch 'v3-2-test' of ssh://jra@git.samba.org/data/git/samba into v3-2-test
[sfrench/samba-autobuild/.git] / source3 / 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 3 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, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "includes.h"
22
23 /****************************************************************************
24  Represent a credential as a string.
25 ****************************************************************************/
26
27 char *credstr(const unsigned char *cred)
28 {
29         char *result;
30         result = talloc_asprintf(talloc_tos(),
31                                  "%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         SMB_ASSERT(result != NULL);
35         return result;
36 }
37
38 /****************************************************************************
39  Setup the session key and the client and server creds in dc.
40  ADS-style 128 bit session keys.
41  Used by both client and server creds setup.
42 ****************************************************************************/
43
44 static void creds_init_128(struct dcinfo *dc,
45                                 const DOM_CHAL *clnt_chal_in,
46                                 const DOM_CHAL *srv_chal_in,
47                                 const unsigned char mach_pw[16])
48 {
49         unsigned char zero[4], tmp[16];
50         HMACMD5Context ctx;
51         struct MD5Context md5;
52
53         /* Just in case this isn't already there */
54         memcpy(dc->mach_pw, mach_pw, 16);
55
56         ZERO_STRUCT(dc->sess_key);
57
58         memset(zero, 0, sizeof(zero));
59
60         hmac_md5_init_rfc2104(mach_pw, 16, &ctx);
61         MD5Init(&md5);
62         MD5Update(&md5, zero, sizeof(zero));
63         MD5Update(&md5, clnt_chal_in->data, 8);
64         MD5Update(&md5, srv_chal_in->data, 8);
65         MD5Final(tmp, &md5);
66         hmac_md5_update(tmp, sizeof(tmp), &ctx);
67         hmac_md5_final(dc->sess_key, &ctx);
68
69         /* debug output */
70         DEBUG(5,("creds_init_128\n"));
71         DEBUG(5,("\tclnt_chal_in: %s\n", credstr(clnt_chal_in->data)));
72         DEBUG(5,("\tsrv_chal_in : %s\n", credstr(srv_chal_in->data)));
73         dump_data_pw("\tsession_key ", (const unsigned char *)dc->sess_key, 16);
74
75         /* Generate the next client and server creds. */
76         
77         des_crypt112(dc->clnt_chal.data,                /* output */
78                         clnt_chal_in->data,             /* input */
79                         dc->sess_key,                   /* input */
80                         1);
81
82         des_crypt112(dc->srv_chal.data,                 /* output */
83                         srv_chal_in->data,              /* input */
84                         dc->sess_key,                   /* input */
85                         1);
86
87         /* Seed is the client chal. */
88         memcpy(dc->seed_chal.data, dc->clnt_chal.data, 8);
89 }
90
91 /****************************************************************************
92  Setup the session key and the client and server creds in dc.
93  Used by both client and server creds setup.
94 ****************************************************************************/
95
96 static void creds_init_64(struct dcinfo *dc,
97                         const DOM_CHAL *clnt_chal_in,
98                         const DOM_CHAL *srv_chal_in,
99                         const unsigned char mach_pw[16])
100 {
101         uint32 sum[2];
102         unsigned char sum2[8];
103
104         /* Just in case this isn't already there */
105         if (dc->mach_pw != mach_pw) {
106                 memcpy(dc->mach_pw, mach_pw, 16);
107         }
108
109         sum[0] = IVAL(clnt_chal_in->data, 0) + IVAL(srv_chal_in->data, 0);
110         sum[1] = IVAL(clnt_chal_in->data, 4) + IVAL(srv_chal_in->data, 4);
111
112         SIVAL(sum2,0,sum[0]);
113         SIVAL(sum2,4,sum[1]);
114
115         ZERO_STRUCT(dc->sess_key);
116
117         des_crypt128(dc->sess_key, sum2, dc->mach_pw);
118
119         /* debug output */
120         DEBUG(5,("creds_init_64\n"));
121         DEBUG(5,("\tclnt_chal_in: %s\n", credstr(clnt_chal_in->data)));
122         DEBUG(5,("\tsrv_chal_in : %s\n", credstr(srv_chal_in->data)));
123         DEBUG(5,("\tclnt+srv : %s\n", credstr(sum2)));
124         DEBUG(5,("\tsess_key_out : %s\n", credstr(dc->sess_key)));
125
126         /* Generate the next client and server creds. */
127         
128         des_crypt112(dc->clnt_chal.data,                /* output */
129                         clnt_chal_in->data,             /* input */
130                         dc->sess_key,                   /* input */
131                         1);
132
133         des_crypt112(dc->srv_chal.data,                 /* output */
134                         srv_chal_in->data,              /* input */
135                         dc->sess_key,                   /* input */
136                         1);
137
138         /* Seed is the client chal. */
139         memcpy(dc->seed_chal.data, dc->clnt_chal.data, 8);
140 }
141
142 /****************************************************************************
143  Utility function to step credential chain one forward.
144  Deliberately doesn't update the seed. See reseed comment below.
145 ****************************************************************************/
146
147 static void creds_step(struct dcinfo *dc)
148 {
149         DOM_CHAL time_chal;
150
151         DEBUG(5,("\tsequence = 0x%x\n", (unsigned int)dc->sequence ));
152
153         DEBUG(5,("\tseed:        %s\n", credstr(dc->seed_chal.data) ));
154
155         SIVAL(time_chal.data, 0, IVAL(dc->seed_chal.data, 0) + dc->sequence);
156         SIVAL(time_chal.data, 4, IVAL(dc->seed_chal.data, 4));
157                                                                                                    
158         DEBUG(5,("\tseed+seq   %s\n", credstr(time_chal.data) ));
159
160         des_crypt112(dc->clnt_chal.data, time_chal.data, dc->sess_key, 1);
161
162         DEBUG(5,("\tCLIENT      %s\n", credstr(dc->clnt_chal.data) ));
163
164         SIVAL(time_chal.data, 0, IVAL(dc->seed_chal.data, 0) + dc->sequence + 1);
165         SIVAL(time_chal.data, 4, IVAL(dc->seed_chal.data, 4));
166
167         DEBUG(5,("\tseed+seq+1   %s\n", credstr(time_chal.data) ));
168
169         des_crypt112(dc->srv_chal.data, time_chal.data, dc->sess_key, 1);
170
171         DEBUG(5,("\tSERVER      %s\n", credstr(dc->srv_chal.data) ));
172 }
173
174 /****************************************************************************
175  Create a server credential struct.
176 ****************************************************************************/
177
178 void creds_server_init(uint32 neg_flags,
179                         struct dcinfo *dc,
180                         DOM_CHAL *clnt_chal,
181                         DOM_CHAL *srv_chal,
182                         const unsigned char mach_pw[16],
183                         DOM_CHAL *init_chal_out)
184 {
185         DEBUG(10,("creds_server_init: neg_flags : %x\n", (unsigned int)neg_flags));
186         DEBUG(10,("creds_server_init: client chal : %s\n", credstr(clnt_chal->data) ));
187         DEBUG(10,("creds_server_init: server chal : %s\n", credstr(srv_chal->data) ));
188         dump_data_pw("creds_server_init: machine pass", mach_pw, 16);
189
190         /* Generate the session key and the next client and server creds. */
191         if (neg_flags & NETLOGON_NEG_128BIT) {
192                 creds_init_128(dc,
193                         clnt_chal,
194                         srv_chal,
195                         mach_pw);
196         } else {
197                 creds_init_64(dc,
198                         clnt_chal,
199                         srv_chal,
200                         mach_pw);
201         }
202
203         dump_data_pw("creds_server_init: session key", dc->sess_key, 16);
204
205         DEBUG(10,("creds_server_init: clnt : %s\n", credstr(dc->clnt_chal.data) ));
206         DEBUG(10,("creds_server_init: server : %s\n", credstr(dc->srv_chal.data) ));
207         DEBUG(10,("creds_server_init: seed : %s\n", credstr(dc->seed_chal.data) ));
208
209         memcpy(init_chal_out->data, dc->srv_chal.data, 8);
210 }
211
212 /****************************************************************************
213  Check a credential sent by the client.
214 ****************************************************************************/
215
216 bool creds_server_check(const struct dcinfo *dc, const DOM_CHAL *rcv_cli_chal_in)
217 {
218         if (memcmp(dc->clnt_chal.data, rcv_cli_chal_in->data, 8)) {
219                 DEBUG(5,("creds_server_check: challenge : %s\n", credstr(rcv_cli_chal_in->data)));
220                 DEBUG(5,("calculated: %s\n", credstr(dc->clnt_chal.data)));
221                 DEBUG(2,("creds_server_check: credentials check failed.\n"));
222                 return False;
223         }
224         DEBUG(10,("creds_server_check: credentials check OK.\n"));
225         return True;
226 }
227
228 bool netlogon_creds_server_check(const struct dcinfo *dc,
229                                  const struct netr_Credential *rcv_cli_chal_in)
230 {
231         if (memcmp(dc->clnt_chal.data, rcv_cli_chal_in->data, 8)) {
232                 DEBUG(5,("netlogon_creds_server_check: challenge : %s\n",
233                         credstr(rcv_cli_chal_in->data)));
234                 DEBUG(5,("calculated: %s\n", credstr(dc->clnt_chal.data)));
235                 DEBUG(2,("netlogon_creds_server_check: credentials check failed.\n"));
236                 return false;
237         }
238
239         DEBUG(10,("netlogon_creds_server_check: credentials check OK.\n"));
240
241         return true;
242 }
243 /****************************************************************************
244  Replace current seed chal. Internal function - due to split server step below.
245 ****************************************************************************/
246
247 static void creds_reseed(struct dcinfo *dc)
248 {
249         DOM_CHAL time_chal;
250
251         SIVAL(time_chal.data, 0, IVAL(dc->seed_chal.data, 0) + dc->sequence + 1);
252         SIVAL(time_chal.data, 4, IVAL(dc->seed_chal.data, 4));
253
254         dc->seed_chal = time_chal;
255
256         DEBUG(5,("cred_reseed: seed %s\n", credstr(dc->seed_chal.data) ));
257 }
258
259 /****************************************************************************
260  Step the server credential chain one forward. 
261 ****************************************************************************/
262
263 bool creds_server_step(struct dcinfo *dc, const DOM_CRED *received_cred, DOM_CRED *cred_out)
264 {
265         bool ret;
266         struct dcinfo tmp_dc = *dc;
267
268         /* Do all operations on a temporary copy of the dc,
269            which we throw away if the checks fail. */
270
271         tmp_dc.sequence = received_cred->timestamp.time;
272
273         creds_step(&tmp_dc);
274
275         /* Create the outgoing credentials */
276         cred_out->timestamp.time = tmp_dc.sequence + 1;
277         cred_out->challenge = tmp_dc.srv_chal;
278
279         creds_reseed(&tmp_dc);
280
281         ret = creds_server_check(&tmp_dc, &received_cred->challenge);
282         if (!ret) {
283                 return False;
284         }
285
286         /* creds step succeeded - replace the current creds. */
287         *dc = tmp_dc;
288         return True;
289 }
290
291 bool netlogon_creds_server_step(struct dcinfo *dc,
292                                 const struct netr_Authenticator *received_cred,
293                                 struct netr_Authenticator *cred_out)
294 {
295         bool ret;
296         struct dcinfo tmp_dc = *dc;
297
298         /* Do all operations on a temporary copy of the dc,
299            which we throw away if the checks fail. */
300
301         tmp_dc.sequence = received_cred->timestamp;
302
303         creds_step(&tmp_dc);
304
305         /* Create the outgoing credentials */
306         cred_out->timestamp = tmp_dc.sequence + 1;
307         memcpy(&cred_out->cred, &tmp_dc.srv_chal, sizeof(cred_out->cred));
308
309         creds_reseed(&tmp_dc);
310
311         ret = netlogon_creds_server_check(&tmp_dc, &received_cred->cred);
312         if (!ret) {
313                 return false;
314         }
315
316         /* creds step succeeded - replace the current creds. */
317         *dc = tmp_dc;
318         return true;
319 }
320
321 /****************************************************************************
322  Create a client credential struct.
323 ****************************************************************************/
324
325 void creds_client_init(uint32 neg_flags,
326                         struct dcinfo *dc,
327                         DOM_CHAL *clnt_chal,
328                         DOM_CHAL *srv_chal,
329                         const unsigned char mach_pw[16],
330                         DOM_CHAL *init_chal_out)
331 {
332         dc->sequence = time(NULL);
333
334         DEBUG(10,("creds_client_init: neg_flags : %x\n", (unsigned int)neg_flags));
335         DEBUG(10,("creds_client_init: client chal : %s\n", credstr(clnt_chal->data) ));
336         DEBUG(10,("creds_client_init: server chal : %s\n", credstr(srv_chal->data) ));
337         dump_data_pw("creds_client_init: machine pass", (const unsigned char *)mach_pw, 16);
338
339         /* Generate the session key and the next client and server creds. */
340         if (neg_flags & NETLOGON_NEG_128BIT) {
341                 creds_init_128(dc,
342                                 clnt_chal,
343                                 srv_chal,
344                                 mach_pw);
345         } else {
346                 creds_init_64(dc,
347                         clnt_chal,
348                         srv_chal,
349                         mach_pw);
350         }
351
352         dump_data_pw("creds_client_init: session key", dc->sess_key, 16);
353
354         DEBUG(10,("creds_client_init: clnt : %s\n", credstr(dc->clnt_chal.data) ));
355         DEBUG(10,("creds_client_init: server : %s\n", credstr(dc->srv_chal.data) ));
356         DEBUG(10,("creds_client_init: seed : %s\n", credstr(dc->seed_chal.data) ));
357
358         memcpy(init_chal_out->data, dc->clnt_chal.data, 8);
359 }
360
361 /****************************************************************************
362  Check a credential returned by the server.
363 ****************************************************************************/
364
365 bool creds_client_check(const struct dcinfo *dc, const DOM_CHAL *rcv_srv_chal_in)
366 {
367         if (memcmp(dc->srv_chal.data, rcv_srv_chal_in->data, 8)) {
368                 DEBUG(5,("creds_client_check: challenge : %s\n", credstr(rcv_srv_chal_in->data)));
369                 DEBUG(5,("calculated: %s\n", credstr(dc->srv_chal.data)));
370                 DEBUG(0,("creds_client_check: credentials check failed.\n"));
371                 return False;
372         }
373         DEBUG(10,("creds_client_check: credentials check OK.\n"));
374         return True;
375 }
376
377 bool netlogon_creds_client_check(const struct dcinfo *dc,
378                                  const struct netr_Credential *rcv_srv_chal_in)
379 {
380         if (memcmp(dc->srv_chal.data, rcv_srv_chal_in->data,
381                    sizeof(dc->srv_chal.data))) {
382
383                 DEBUG(0,("netlogon_creds_client_check: credentials check failed.\n"));
384                 DEBUGADD(5,("netlogon_creds_client_check: challenge : %s\n",
385                         credstr(rcv_srv_chal_in->data)));
386                 DEBUGADD(5,("calculated: %s\n", credstr(dc->srv_chal.data)));
387                 return false;
388         }
389
390         DEBUG(10,("netlogon_creds_client_check: credentials check OK.\n"));
391
392         return true;
393 }
394
395
396 /****************************************************************************
397   Step the client credentials to the next element in the chain, updating the
398   current client and server credentials and the seed
399   produce the next authenticator in the sequence ready to send to
400   the server
401 ****************************************************************************/
402
403 void creds_client_step(struct dcinfo *dc, DOM_CRED *next_cred_out)
404 {
405         dc->sequence += 2;
406         creds_step(dc);
407         creds_reseed(dc);
408
409         next_cred_out->challenge = dc->clnt_chal;
410         next_cred_out->timestamp.time = dc->sequence;
411 }
412
413 void netlogon_creds_client_step(struct dcinfo *dc,
414                                 struct netr_Authenticator *next_cred_out)
415 {
416         dc->sequence += 2;
417         creds_step(dc);
418         creds_reseed(dc);
419
420         memcpy(&next_cred_out->cred.data, &dc->clnt_chal.data,
421                 sizeof(next_cred_out->cred.data));
422         next_cred_out->timestamp = dc->sequence;
423 }