r13407: Change the credentials code to be more like the Samba4 structure,
[samba.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 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 uchar *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 /****************************************************************************
39  Setup the session key and the client and server creds in dc.
40  Used by both client and server creds setup.
41 ****************************************************************************/
42
43 static void creds_init_64(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         uint32 sum[2];
49         unsigned char sum2[8];
50
51         /* Just in case this isn't already there */
52         memcpy(dc->mach_pw, mach_pw, 16);
53
54         sum[0] = IVAL(clnt_chal_in->data, 0) + IVAL(srv_chal_in->data, 0);
55         sum[1] = IVAL(clnt_chal_in->data, 4) + IVAL(srv_chal_in->data, 4);
56
57         SIVAL(sum2,0,sum[0]);
58         SIVAL(sum2,4,sum[1]);
59
60         ZERO_STRUCT(dc->sess_key);
61
62         des_crypt128(dc->sess_key, sum2, dc->mach_pw);
63
64         /* debug output */
65         DEBUG(5,("creds_init_64\n"));
66         DEBUG(5,("      clnt_chal_in: %s\n", credstr(clnt_chal_in->data)));
67         DEBUG(5,("      srv_chal_in : %s\n", credstr(srv_chal_in->data)));
68         DEBUG(5,("      clnt+srv : %s\n", credstr(sum2)));
69         DEBUG(5,("      sess_key_out : %s\n", credstr(dc->sess_key)));
70
71         /* Generate the next client and server creds. */
72         
73         des_crypt112(dc->clnt_chal.data,                /* output */
74                         clnt_chal_in->data,             /* input */
75                         dc->sess_key,                   /* input */
76                         1);
77
78         des_crypt112(dc->srv_chal.data,                 /* output */
79                         srv_chal_in->data,              /* input */
80                         dc->sess_key,                   /* input */
81                         1);
82
83         /* Seed is the client chal. */
84         memcpy(dc->seed_chal.data, dc->clnt_chal.data, 8);
85 }
86
87 /****************************************************************************
88  Utility function to step credential chain one forward.
89  Deliberately doesn't update the seed. See reseed comment below.
90 ****************************************************************************/
91
92 static void creds_step(struct dcinfo *dc)
93 {
94         DOM_CHAL time_chal;
95
96         DEBUG(5,("\tsequence = 0x%x\n", (unsigned int)dc->sequence ));
97
98         DEBUG(5,("\tseed:        %s\n", credstr(dc->seed_chal.data) ));
99
100         SIVAL(time_chal.data, 0, IVAL(dc->seed_chal.data, 0) + dc->sequence);
101         SIVAL(time_chal.data, 4, IVAL(dc->seed_chal.data, 4));
102                                                                                                    
103         DEBUG(5,("\tseed+seq   %s\n", credstr(time_chal.data) ));
104
105         des_crypt112(dc->clnt_chal.data, time_chal.data, dc->sess_key, 1);
106
107         DEBUG(5,("\tCLIENT      %s\n", credstr(dc->clnt_chal.data) ));
108
109         SIVAL(time_chal.data, 0, IVAL(dc->seed_chal.data, 0) + dc->sequence + 1);
110         SIVAL(time_chal.data, 4, IVAL(dc->seed_chal.data, 4));
111
112         DEBUG(5,("\tseed+seq+1   %s\n", credstr(time_chal.data) ));
113
114         des_crypt112(dc->srv_chal.data, time_chal.data, dc->sess_key, 1);
115
116         DEBUG(5,("\tSERVER      %s\n", credstr(dc->srv_chal.data) ));
117 }
118
119 /****************************************************************************
120  Create a server credential struct.
121 ****************************************************************************/
122
123 void creds_server_init(struct dcinfo *dc,
124                         DOM_CHAL *clnt_chal,
125                         DOM_CHAL *srv_chal,
126                         const char mach_pw[16],
127                         DOM_CHAL *init_chal_out)
128 {
129         DEBUG(10,("creds_server_init: client chal : %s\n", credstr(clnt_chal->data) ));
130         DEBUG(10,("creds_server_init: server chal : %s\n", credstr(srv_chal->data) ));
131         dump_data_pw("creds_server_init: machine pass", (const unsigned char *)mach_pw, 16);
132
133         /* Generate the session key and the next client and server creds. */
134         creds_init_64(dc,
135                         clnt_chal,
136                         srv_chal,
137                         mach_pw);
138
139         dump_data_pw("creds_server_init: session key", dc->sess_key, 16);
140
141         DEBUG(10,("creds_server_init: clnt : %s\n", credstr(dc->clnt_chal.data) ));
142         DEBUG(10,("creds_server_init: server : %s\n", credstr(dc->srv_chal.data) ));
143         DEBUG(10,("creds_server_init: seed : %s\n", credstr(dc->seed_chal.data) ));
144
145         memcpy(init_chal_out->data, dc->srv_chal.data, 8);
146 }
147
148 /****************************************************************************
149  Check a credential sent by the client.
150 ****************************************************************************/
151
152 BOOL creds_server_check(const struct dcinfo *dc, const DOM_CHAL *rcv_cli_chal_in)
153 {
154         if (memcmp(dc->clnt_chal.data, rcv_cli_chal_in->data, 8)) {
155                 DEBUG(5,("creds_server_check: challenge : %s\n", credstr(rcv_cli_chal_in->data)));
156                 DEBUG(5,("calculated: %s\n", credstr(dc->clnt_chal.data)));
157                 DEBUG(2,("creds_server_check: credentials check failed.\n"));
158                 return False;
159         }
160         DEBUG(10,("creds_server_check: credentials check OK.\n"));
161         return True;
162 }
163
164 /****************************************************************************
165  Replace current seed chal. Internal function - due to split server step below.
166 ****************************************************************************/
167
168 static void creds_reseed(struct dcinfo *dc)
169 {
170         DOM_CHAL time_chal;
171
172         SIVAL(time_chal.data, 0, IVAL(dc->seed_chal.data, 0) + dc->sequence + 1);
173         SIVAL(time_chal.data, 4, IVAL(dc->seed_chal.data, 4));
174
175         dc->seed_chal = time_chal;
176
177         DEBUG(5,("cred_reseed: seed %s\n", credstr(dc->seed_chal.data) ));
178 }
179
180 /****************************************************************************
181  Step the server credential chain one forward. 
182 ****************************************************************************/
183
184 BOOL creds_server_step(struct dcinfo *dc, const DOM_CRED *received_cred, DOM_CRED *cred_out)
185 {
186         dc->sequence = received_cred->timestamp.time;
187
188         creds_step(dc);
189
190         /* Create the outgoing credentials */
191         cred_out->timestamp.time = dc->sequence + 1;
192         cred_out->challenge = dc->srv_chal;
193
194         creds_reseed(dc);
195
196         return creds_server_check(dc, &received_cred->challenge);
197 }
198
199 /****************************************************************************
200  Create a client credential struct.
201 ****************************************************************************/
202
203 void creds_client_init(struct dcinfo *dc,
204                         DOM_CHAL *clnt_chal,
205                         DOM_CHAL *srv_chal,
206                         const unsigned char mach_pw[16],
207                         DOM_CHAL *init_chal_out)
208 {
209         dc->sequence = time(NULL);
210
211         DEBUG(10,("creds_client_init: client chal : %s\n", credstr(clnt_chal->data) ));
212         DEBUG(10,("creds_client_init: server chal : %s\n", credstr(srv_chal->data) ));
213         dump_data_pw("creds_client_init: machine pass", (const unsigned char *)mach_pw, 16);
214
215         /* Generate the session key and the next client and server creds. */
216         creds_init_64(dc,
217                         clnt_chal,
218                         srv_chal,
219                         mach_pw);
220
221         dump_data_pw("creds_client_init: session key", dc->sess_key, 16);
222
223         DEBUG(10,("creds_client_init: clnt : %s\n", credstr(dc->clnt_chal.data) ));
224         DEBUG(10,("creds_client_init: server : %s\n", credstr(dc->srv_chal.data) ));
225         DEBUG(10,("creds_client_init: seed : %s\n", credstr(dc->seed_chal.data) ));
226
227         memcpy(init_chal_out->data, dc->clnt_chal.data, 8);
228 }
229
230 /****************************************************************************
231  Check a credential returned by the server.
232 ****************************************************************************/
233
234 BOOL creds_client_check(const struct dcinfo *dc, const DOM_CHAL *rcv_srv_chal_in)
235 {
236         if (memcmp(dc->srv_chal.data, rcv_srv_chal_in->data, 8)) {
237                 DEBUG(5,("creds_client_check: challenge : %s\n", credstr(rcv_srv_chal_in->data)));
238                 DEBUG(5,("calculated: %s\n", credstr(dc->srv_chal.data)));
239                 DEBUG(0,("creds_client_check: credentials check failed.\n"));
240                 return False;
241         }
242         DEBUG(10,("creds_client_check: credentials check OK.\n"));
243         return True;
244 }
245
246 /****************************************************************************
247   Step the client credentials to the next element in the chain, updating the
248   current client and server credentials and the seed
249   produce the next authenticator in the sequence ready to send to
250   the server
251 ****************************************************************************/
252
253 void creds_client_step(struct dcinfo *dc, DOM_CRED *next_cred_out)
254 {
255         dc->sequence += 2;
256         creds_step(dc);
257         creds_reseed(dc);
258
259         next_cred_out->challenge = dc->clnt_chal;
260         next_cred_out->timestamp.time = dc->sequence;
261 }