Fix bug #6040 - Calling Samba print server with an aliased DNS-name fails.
[Samba.git] / source / libsmb / credentials.c
blob9d33e6d93d7298070da5092a409f71e6a60bcdac
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.
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.
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.
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/>.
21 #include "includes.h"
23 /****************************************************************************
24 Represent a credential as a string.
25 ****************************************************************************/
27 char *credstr(const unsigned char *cred)
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;
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 ****************************************************************************/
44 static void creds_init_128(struct dcinfo *dc,
45 const struct netr_Credential *clnt_chal_in,
46 const struct netr_Credential *srv_chal_in,
47 const unsigned char mach_pw[16])
49 unsigned char zero[4], tmp[16];
50 HMACMD5Context ctx;
51 struct MD5Context md5;
53 /* Just in case this isn't already there */
54 memcpy(dc->mach_pw, mach_pw, 16);
56 ZERO_STRUCT(dc->sess_key);
58 memset(zero, 0, sizeof(zero));
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);
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);
75 /* Generate the next client and server creds. */
77 des_crypt112(dc->clnt_chal.data, /* output */
78 clnt_chal_in->data, /* input */
79 dc->sess_key, /* input */
80 1);
82 des_crypt112(dc->srv_chal.data, /* output */
83 srv_chal_in->data, /* input */
84 dc->sess_key, /* input */
85 1);
87 /* Seed is the client chal. */
88 memcpy(dc->seed_chal.data, dc->clnt_chal.data, 8);
91 /****************************************************************************
92 Setup the session key and the client and server creds in dc.
93 Used by both client and server creds setup.
94 ****************************************************************************/
96 static void creds_init_64(struct dcinfo *dc,
97 const struct netr_Credential *clnt_chal_in,
98 const struct netr_Credential *srv_chal_in,
99 const unsigned char mach_pw[16])
101 uint32 sum[2];
102 unsigned char sum2[8];
104 /* Just in case this isn't already there */
105 if (dc->mach_pw != mach_pw) {
106 memcpy(dc->mach_pw, mach_pw, 16);
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);
112 SIVAL(sum2,0,sum[0]);
113 SIVAL(sum2,4,sum[1]);
115 ZERO_STRUCT(dc->sess_key);
117 des_crypt128(dc->sess_key, sum2, dc->mach_pw);
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)));
126 /* Generate the next client and server creds. */
128 des_crypt112(dc->clnt_chal.data, /* output */
129 clnt_chal_in->data, /* input */
130 dc->sess_key, /* input */
133 des_crypt112(dc->srv_chal.data, /* output */
134 srv_chal_in->data, /* input */
135 dc->sess_key, /* input */
138 /* Seed is the client chal. */
139 memcpy(dc->seed_chal.data, dc->clnt_chal.data, 8);
142 /****************************************************************************
143 Utility function to step credential chain one forward.
144 Deliberately doesn't update the seed. See reseed comment below.
145 ****************************************************************************/
147 static void creds_step(struct dcinfo *dc)
149 DOM_CHAL time_chal;
151 DEBUG(5,("\tsequence = 0x%x\n", (unsigned int)dc->sequence ));
153 DEBUG(5,("\tseed: %s\n", credstr(dc->seed_chal.data) ));
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));
158 DEBUG(5,("\tseed+seq %s\n", credstr(time_chal.data) ));
160 des_crypt112(dc->clnt_chal.data, time_chal.data, dc->sess_key, 1);
162 DEBUG(5,("\tCLIENT %s\n", credstr(dc->clnt_chal.data) ));
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));
167 DEBUG(5,("\tseed+seq+1 %s\n", credstr(time_chal.data) ));
169 des_crypt112(dc->srv_chal.data, time_chal.data, dc->sess_key, 1);
171 DEBUG(5,("\tSERVER %s\n", credstr(dc->srv_chal.data) ));
174 /****************************************************************************
175 Create a server credential struct.
176 ****************************************************************************/
178 void creds_server_init(uint32 neg_flags,
179 struct dcinfo *dc,
180 struct netr_Credential *clnt_chal,
181 struct netr_Credential *srv_chal,
182 const unsigned char mach_pw[16],
183 struct netr_Credential *init_chal_out)
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);
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);
203 dump_data_pw("creds_server_init: session key", dc->sess_key, 16);
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) ));
209 memcpy(init_chal_out->data, dc->srv_chal.data, 8);
212 /****************************************************************************
213 Check a credential sent by the client.
214 ****************************************************************************/
216 bool netlogon_creds_server_check(const struct dcinfo *dc,
217 const struct netr_Credential *rcv_cli_chal_in)
219 if (memcmp(dc->clnt_chal.data, rcv_cli_chal_in->data, 8)) {
220 DEBUG(5,("netlogon_creds_server_check: challenge : %s\n",
221 credstr(rcv_cli_chal_in->data)));
222 DEBUG(5,("calculated: %s\n", credstr(dc->clnt_chal.data)));
223 DEBUG(2,("netlogon_creds_server_check: credentials check failed.\n"));
224 return false;
227 DEBUG(10,("netlogon_creds_server_check: credentials check OK.\n"));
229 return true;
231 /****************************************************************************
232 Replace current seed chal. Internal function - due to split server step below.
233 ****************************************************************************/
235 static void creds_reseed(struct dcinfo *dc)
237 struct netr_Credential time_chal;
239 SIVAL(time_chal.data, 0, IVAL(dc->seed_chal.data, 0) + dc->sequence + 1);
240 SIVAL(time_chal.data, 4, IVAL(dc->seed_chal.data, 4));
242 dc->seed_chal = time_chal;
244 DEBUG(5,("cred_reseed: seed %s\n", credstr(dc->seed_chal.data) ));
247 /****************************************************************************
248 Step the server credential chain one forward.
249 ****************************************************************************/
251 bool netlogon_creds_server_step(struct dcinfo *dc,
252 const struct netr_Authenticator *received_cred,
253 struct netr_Authenticator *cred_out)
255 bool ret;
256 struct dcinfo tmp_dc = *dc;
258 /* Do all operations on a temporary copy of the dc,
259 which we throw away if the checks fail. */
261 tmp_dc.sequence = received_cred->timestamp;
263 creds_step(&tmp_dc);
265 /* Create the outgoing credentials */
266 cred_out->timestamp = tmp_dc.sequence + 1;
267 memcpy(&cred_out->cred, &tmp_dc.srv_chal, sizeof(cred_out->cred));
269 creds_reseed(&tmp_dc);
271 ret = netlogon_creds_server_check(&tmp_dc, &received_cred->cred);
272 if (!ret) {
273 return false;
276 /* creds step succeeded - replace the current creds. */
277 *dc = tmp_dc;
278 return true;
281 /****************************************************************************
282 Create a client credential struct.
283 ****************************************************************************/
285 void creds_client_init(uint32 neg_flags,
286 struct dcinfo *dc,
287 struct netr_Credential *clnt_chal,
288 struct netr_Credential *srv_chal,
289 const unsigned char mach_pw[16],
290 struct netr_Credential *init_chal_out)
292 dc->sequence = time(NULL);
294 DEBUG(10,("creds_client_init: neg_flags : %x\n", (unsigned int)neg_flags));
295 DEBUG(10,("creds_client_init: client chal : %s\n", credstr(clnt_chal->data) ));
296 DEBUG(10,("creds_client_init: server chal : %s\n", credstr(srv_chal->data) ));
297 dump_data_pw("creds_client_init: machine pass", (const unsigned char *)mach_pw, 16);
299 /* Generate the session key and the next client and server creds. */
300 if (neg_flags & NETLOGON_NEG_128BIT) {
301 creds_init_128(dc,
302 clnt_chal,
303 srv_chal,
304 mach_pw);
305 } else {
306 creds_init_64(dc,
307 clnt_chal,
308 srv_chal,
309 mach_pw);
312 dump_data_pw("creds_client_init: session key", dc->sess_key, 16);
314 DEBUG(10,("creds_client_init: clnt : %s\n", credstr(dc->clnt_chal.data) ));
315 DEBUG(10,("creds_client_init: server : %s\n", credstr(dc->srv_chal.data) ));
316 DEBUG(10,("creds_client_init: seed : %s\n", credstr(dc->seed_chal.data) ));
318 memcpy(init_chal_out->data, dc->clnt_chal.data, 8);
321 /****************************************************************************
322 Check a credential returned by the server.
323 ****************************************************************************/
325 bool netlogon_creds_client_check(const struct dcinfo *dc,
326 const struct netr_Credential *rcv_srv_chal_in)
328 if (memcmp(dc->srv_chal.data, rcv_srv_chal_in->data,
329 sizeof(dc->srv_chal.data))) {
331 DEBUG(0,("netlogon_creds_client_check: credentials check failed.\n"));
332 DEBUGADD(5,("netlogon_creds_client_check: challenge : %s\n",
333 credstr(rcv_srv_chal_in->data)));
334 DEBUGADD(5,("calculated: %s\n", credstr(dc->srv_chal.data)));
335 return false;
338 DEBUG(10,("netlogon_creds_client_check: credentials check OK.\n"));
340 return true;
344 /****************************************************************************
345 Step the client credentials to the next element in the chain, updating the
346 current client and server credentials and the seed
347 produce the next authenticator in the sequence ready to send to
348 the server
349 ****************************************************************************/
351 void netlogon_creds_client_step(struct dcinfo *dc,
352 struct netr_Authenticator *next_cred_out)
354 dc->sequence += 2;
355 creds_step(dc);
356 creds_reseed(dc);
358 memcpy(&next_cred_out->cred.data, &dc->clnt_chal.data,
359 sizeof(next_cred_out->cred.data));
360 next_cred_out->timestamp = dc->sequence;