Refactor the NTLMSSP code again - this time we use function pointers to
[kai/samba.git] / source3 / libsmb / ntlmssp.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 3.0
4    handle NLTMSSP, server side
5
6    Copyright (C) Andrew Tridgell      2001
7    Copyright (C) Andrew Bartlett 2001-2003
8
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 2 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23
24 #include "includes.h"
25
26 /**
27  * Default challange generation code.
28  *
29  */
30    
31
32 static const uint8 *get_challenge(void *cookie)
33 {
34         static uchar chal[8];
35         generate_random_buffer(chal, sizeof(chal), False);
36
37         return chal;
38 }
39
40 NTSTATUS ntlmssp_server_start(NTLMSSP_STATE **ntlmssp_state)
41 {
42         TALLOC_CTX *mem_ctx;
43
44         mem_ctx = talloc_init("NTLMSSP context");
45         
46         *ntlmssp_state = talloc_zero(mem_ctx, sizeof(**ntlmssp_state));
47         if (!*ntlmssp_state) {
48                 DEBUG(0,("ntlmssp_start: talloc failed!\n"));
49                 talloc_destroy(mem_ctx);
50                 return NT_STATUS_NO_MEMORY;
51         }
52
53         ZERO_STRUCTP(*ntlmssp_state);
54
55         (*ntlmssp_state)->mem_ctx = mem_ctx;
56         (*ntlmssp_state)->get_challenge = get_challenge;
57
58         (*ntlmssp_state)->get_global_myname = global_myname;
59         (*ntlmssp_state)->get_domain = lp_workgroup;
60
61         return NT_STATUS_OK;
62 }
63
64 NTSTATUS ntlmssp_server_end(NTLMSSP_STATE **ntlmssp_state)
65 {
66         TALLOC_CTX *mem_ctx = (*ntlmssp_state)->mem_ctx;
67
68         data_blob_free(&(*ntlmssp_state)->lm_resp);
69         data_blob_free(&(*ntlmssp_state)->nt_resp);
70
71         SAFE_FREE((*ntlmssp_state)->user);
72         SAFE_FREE((*ntlmssp_state)->domain);
73         SAFE_FREE((*ntlmssp_state)->workstation);
74
75         talloc_destroy(mem_ctx);
76         *ntlmssp_state = NULL;
77         return NT_STATUS_OK;
78 }
79
80 NTSTATUS ntlmssp_server_update(NTLMSSP_STATE *ntlmssp_state, 
81                                DATA_BLOB request, DATA_BLOB *reply) 
82 {
83         uint32 ntlmssp_command;
84                 
85         if (!msrpc_parse(&request, "Cd",
86                          "NTLMSSP",
87                          &ntlmssp_command)) {
88                 return NT_STATUS_LOGON_FAILURE;
89         }
90
91         if (ntlmssp_command == NTLMSSP_NEGOTIATE) {
92                 return ntlmssp_negotiate(ntlmssp_state, request, reply);
93         } else if (ntlmssp_command == NTLMSSP_AUTH) {
94                 return ntlmssp_auth(ntlmssp_state, request, reply);
95         } else {
96                 return NT_STATUS_LOGON_FAILURE;
97         }
98 }
99
100 static const char *ntlmssp_target_name(NTLMSSP_STATE *ntlmssp_state, 
101                                        uint32 neg_flags, uint32 *chal_flags) 
102 {
103         if (neg_flags & NTLMSSP_REQUEST_TARGET) {
104                 *chal_flags |= NTLMSSP_CHAL_TARGET_INFO;
105                 *chal_flags |= NTLMSSP_REQUEST_TARGET;
106                 if (lp_server_role() == ROLE_STANDALONE) {
107                         *chal_flags |= NTLMSSP_TARGET_TYPE_SERVER;
108                         return ntlmssp_state->get_global_myname();
109                 } else {
110                         *chal_flags |= NTLMSSP_TARGET_TYPE_DOMAIN;
111                         return ntlmssp_state->get_domain();
112                 };
113         } else {
114                 return "";
115         }
116 }
117
118 NTSTATUS ntlmssp_negotiate(NTLMSSP_STATE *ntlmssp_state, 
119                            DATA_BLOB request, DATA_BLOB *reply) 
120 {
121         DATA_BLOB struct_blob;
122         fstring dnsname, dnsdomname;
123         uint32 ntlmssp_command, neg_flags, chal_flags;
124         char *cliname=NULL, *domname=NULL;
125         const uint8 *cryptkey;
126         const char *target_name;
127
128         /* parse the NTLMSSP packet */
129 #if 0
130         file_save("ntlmssp_negotiate.dat", request.data, request.length);
131 #endif
132
133         if (!msrpc_parse(&request, "CddAA",
134                          "NTLMSSP",
135                          &ntlmssp_command,
136                          &neg_flags,
137                          &cliname,
138                          &domname)) {
139                 return NT_STATUS_LOGON_FAILURE;
140         }
141
142         SAFE_FREE(cliname);
143         SAFE_FREE(domname);
144   
145         debug_ntlmssp_flags(neg_flags);
146
147         cryptkey = ntlmssp_state->get_challenge(ntlmssp_state->auth_context);
148
149         /* Give them the challenge. For now, ignore neg_flags and just
150            return the flags we want. Obviously this is not correct */
151         
152         chal_flags = 
153                 NTLMSSP_NEGOTIATE_128 | 
154                 NTLMSSP_NEGOTIATE_NTLM;
155         
156         if (neg_flags & NTLMSSP_NEGOTIATE_UNICODE) {
157                 chal_flags |= NTLMSSP_NEGOTIATE_UNICODE;
158                 ntlmssp_state->unicode = True;
159         } else {
160                 chal_flags |= NTLMSSP_NEGOTIATE_OEM;
161         }
162
163         target_name = ntlmssp_target_name(ntlmssp_state, 
164                                           neg_flags, &chal_flags); 
165
166         /* This should be a 'netbios domain -> DNS domain' mapping */
167         dnsdomname[0] = '\0';
168         get_mydomname(dnsdomname);
169         strlower(dnsdomname);
170         
171         dnsname[0] = '\0';
172         get_myfullname(dnsname);
173         strlower(dnsname);
174         
175         if (chal_flags & NTLMSSP_CHAL_TARGET_INFO) 
176         {
177                 const char *target_name_dns = "";
178                 if (chal_flags |= NTLMSSP_TARGET_TYPE_DOMAIN) {
179                         target_name_dns = dnsdomname;
180                 } else if (chal_flags |= NTLMSSP_TARGET_TYPE_SERVER) {
181                         target_name_dns = dnsname;
182                 }
183
184                 /* the numbers here are the string type flags */
185                 msrpc_gen(&struct_blob, "aaaaa",
186                           ntlmssp_state->unicode, NTLMSSP_NAME_TYPE_DOMAIN, target_name,
187                           ntlmssp_state->unicode, NTLMSSP_NAME_TYPE_SERVER, ntlmssp_state->get_global_myname(),
188                           ntlmssp_state->unicode, NTLMSSP_NAME_TYPE_DOMAIN_DNS, target_name_dns,
189                           ntlmssp_state->unicode, NTLMSSP_NAME_TYPE_SERVER_DNS, dnsdomname,
190                           ntlmssp_state->unicode, 0, "");
191         } else {
192                 struct_blob = data_blob(NULL, 0);
193         }
194
195         {
196                 const char *gen_string;
197                 if (ntlmssp_state->unicode) {
198                         gen_string = "CdUdbddB";
199                 } else {
200                         gen_string = "CdAdbddB";
201                 }
202                 
203                 msrpc_gen(reply, gen_string,
204                           "NTLMSSP", 
205                           NTLMSSP_CHALLENGE,
206                           target_name,
207                           chal_flags,
208                           cryptkey, 8,
209                           0, 0,
210                           struct_blob.data, struct_blob.length);
211         }
212                 
213         data_blob_free(&struct_blob);
214
215         return NT_STATUS_MORE_PROCESSING_REQUIRED;
216 }
217
218 NTSTATUS ntlmssp_auth(NTLMSSP_STATE *ntlmssp_state, 
219                       DATA_BLOB request, DATA_BLOB *reply) 
220 {
221         DATA_BLOB sess_key;
222         uint32 ntlmssp_command, neg_flags;
223         NTSTATUS nt_status;
224
225         const char *parse_string;
226
227         /* parse the NTLMSSP packet */
228 #if 0
229         file_save("ntlmssp_auth.dat", request.data, request.length);
230 #endif
231
232         if (ntlmssp_state->unicode) {
233                 parse_string = "CdBBUUUBd";
234         } else {
235                 parse_string = "CdBBAAABd";
236         }
237
238         data_blob_free(&ntlmssp_state->lm_resp);
239         data_blob_free(&ntlmssp_state->nt_resp);
240
241         SAFE_FREE(ntlmssp_state->user);
242         SAFE_FREE(ntlmssp_state->domain);
243         SAFE_FREE(ntlmssp_state->workstation);
244
245         /* now the NTLMSSP encoded auth hashes */
246         if (!msrpc_parse(&request, parse_string,
247                          "NTLMSSP", 
248                          &ntlmssp_command, 
249                          &ntlmssp_state->lm_resp,
250                          &ntlmssp_state->nt_resp,
251                          &ntlmssp_state->domain, 
252                          &ntlmssp_state->user, 
253                          &ntlmssp_state->workstation,
254                          &sess_key,
255                          &neg_flags)) {
256                 return NT_STATUS_LOGON_FAILURE;
257         }
258
259         data_blob_free(&sess_key);
260         
261         DEBUG(3,("Got user=[%s] domain=[%s] workstation=[%s] len1=%d len2=%d\n",
262                  ntlmssp_state->user, ntlmssp_state->domain, ntlmssp_state->workstation, ntlmssp_state->lm_resp.length, ntlmssp_state->nt_resp.length));
263
264 #if 0
265         file_save("nthash1.dat",  &ntlmssp_state->nt_resp.data,  &ntlmssp_state->nt_resp.length);
266         file_save("lmhash1.dat",  &ntlmssp_state->lm_resp.data,  &ntlmssp_state->lm_resp.length);
267 #endif
268
269         nt_status = ntlmssp_state->check_password(ntlmssp_state->auth_context);
270         
271         if (!NT_STATUS_IS_OK(nt_status)) {
272                 return nt_status;
273         }
274
275         *reply = data_blob(NULL, 0);
276
277         return nt_status;
278 }