/src/samba/source4/auth/ntlm/auth_winbind.c
Line | Count | Source |
1 | | /* |
2 | | Unix SMB/CIFS implementation. |
3 | | |
4 | | Winbind authentication mechanism |
5 | | |
6 | | Copyright (C) Tim Potter 2000 |
7 | | Copyright (C) Andrew Bartlett 2001 - 2002 |
8 | | Copyright (C) Stefan Metzmacher 2005 |
9 | | |
10 | | This program is free software; you can redistribute it and/or modify |
11 | | it under the terms of the GNU General Public License as published by |
12 | | the Free Software Foundation; either version 3 of the License, or |
13 | | (at your option) any later version. |
14 | | |
15 | | This program is distributed in the hope that it will be useful, |
16 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
17 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
18 | | GNU General Public License for more details. |
19 | | |
20 | | You should have received a copy of the GNU General Public License |
21 | | along with this program. If not, see <http://www.gnu.org/licenses/>. |
22 | | */ |
23 | | |
24 | | #include "includes.h" |
25 | | #include <tevent.h> |
26 | | #include "../lib/util/tevent_ntstatus.h" |
27 | | #include "auth/auth.h" |
28 | | #include "auth/ntlm/auth_proto.h" |
29 | | #include "librpc/gen_ndr/ndr_winbind_c.h" |
30 | | #include "lib/messaging/irpc.h" |
31 | | #include "param/param.h" |
32 | | #include "nsswitch/libwbclient/wbclient.h" |
33 | | #include "auth/auth_sam_reply.h" |
34 | | #include "libcli/security/security.h" |
35 | | #include "dsdb/samdb/samdb.h" |
36 | | #include "auth/auth_sam.h" |
37 | | |
38 | | #undef DBGC_CLASS |
39 | 0 | #define DBGC_CLASS DBGC_AUTH |
40 | | |
41 | | _PUBLIC_ NTSTATUS auth4_winbind_init(TALLOC_CTX *); |
42 | | |
43 | | static NTSTATUS winbind_want_check(struct auth_method_context *ctx, |
44 | | TALLOC_CTX *mem_ctx, |
45 | | const struct auth_usersupplied_info *user_info) |
46 | 0 | { |
47 | 0 | if (!user_info->mapped.account_name || !*user_info->mapped.account_name) { |
48 | 0 | return NT_STATUS_NOT_IMPLEMENTED; |
49 | 0 | } |
50 | | |
51 | | /* TODO: maybe limit the user scope to remote users only */ |
52 | 0 | return NT_STATUS_OK; |
53 | 0 | } |
54 | | |
55 | | struct winbind_check_password_state { |
56 | | struct auth_method_context *ctx; |
57 | | const struct auth_usersupplied_info *user_info; |
58 | | struct winbind_SamLogon req; |
59 | | struct auth_user_info_dc *user_info_dc; |
60 | | bool authoritative; |
61 | | }; |
62 | | |
63 | | static void winbind_check_password_done(struct tevent_req *subreq); |
64 | | |
65 | | /* |
66 | | Authenticate a user with a challenge/response |
67 | | using IRPC to the winbind task |
68 | | */ |
69 | | static struct tevent_req *winbind_check_password_send(TALLOC_CTX *mem_ctx, |
70 | | struct tevent_context *ev, |
71 | | struct auth_method_context *ctx, |
72 | | const struct auth_usersupplied_info *user_info) |
73 | 0 | { |
74 | 0 | struct tevent_req *req = NULL; |
75 | 0 | struct winbind_check_password_state *state = NULL; |
76 | 0 | NTSTATUS status; |
77 | 0 | struct dcerpc_binding_handle *irpc_handle; |
78 | 0 | const struct auth_usersupplied_info *user_info_new; |
79 | 0 | struct netr_IdentityInfo *identity_info; |
80 | 0 | struct imessaging_context *msg_ctx; |
81 | 0 | struct tevent_req *subreq = NULL; |
82 | |
|
83 | 0 | req = tevent_req_create(mem_ctx, &state, |
84 | 0 | struct winbind_check_password_state); |
85 | 0 | if (req == NULL) { |
86 | 0 | return NULL; |
87 | 0 | } |
88 | 0 | state->ctx = ctx; |
89 | 0 | state->user_info = user_info; |
90 | 0 | state->authoritative = true; |
91 | |
|
92 | 0 | if (ctx->auth_ctx->for_netlogon) { |
93 | 0 | state->req.in.internal_flags |= WB_SAMLOGON_FOR_NETLOGON; |
94 | 0 | } |
95 | |
|
96 | 0 | msg_ctx = imessaging_client_init(state, ctx->auth_ctx->lp_ctx, ev); |
97 | 0 | if (msg_ctx == NULL) { |
98 | 0 | DEBUG(1, ("imessaging_init failed\n")); |
99 | 0 | tevent_req_nterror(req, NT_STATUS_INVALID_SERVER_STATE); |
100 | 0 | return tevent_req_post(req, ev); |
101 | 0 | } |
102 | | |
103 | 0 | irpc_handle = irpc_binding_handle_by_name(state, msg_ctx, |
104 | 0 | "winbind_server", |
105 | 0 | &ndr_table_winbind); |
106 | 0 | if (irpc_handle == NULL) { |
107 | 0 | DEBUG(0, ("Winbind authentication for [%s]\\[%s] failed, " |
108 | 0 | "no winbind_server running!\n", |
109 | 0 | user_info->client.domain_name, user_info->client.account_name)); |
110 | 0 | tevent_req_nterror(req, NT_STATUS_NO_LOGON_SERVERS); |
111 | 0 | return tevent_req_post(req, ev); |
112 | 0 | } |
113 | | |
114 | | /* |
115 | | * 120 seconds should be enough even for trusted domains. |
116 | | * |
117 | | * Currently winbindd has a much lower limit. |
118 | | * And tests with Windows RODCs show that it |
119 | | * returns NO_LOGON_SERVERS after 90-100 seconds |
120 | | * if it can't reach any RWDC. |
121 | | */ |
122 | 0 | dcerpc_binding_handle_set_timeout(irpc_handle, 120); |
123 | |
|
124 | 0 | if (user_info->flags & USER_INFO_INTERACTIVE_LOGON) { |
125 | 0 | struct netr_PasswordInfo *password_info; |
126 | |
|
127 | 0 | status = encrypt_user_info(state, ctx->auth_ctx, AUTH_PASSWORD_HASH, |
128 | 0 | user_info, &user_info_new); |
129 | 0 | if (tevent_req_nterror(req, status)) { |
130 | 0 | return tevent_req_post(req, ev); |
131 | 0 | } |
132 | 0 | user_info = user_info_new; |
133 | |
|
134 | 0 | password_info = talloc_zero(state, struct netr_PasswordInfo); |
135 | 0 | if (tevent_req_nomem(password_info, req)) { |
136 | 0 | return tevent_req_post(req, ev); |
137 | 0 | } |
138 | | |
139 | 0 | password_info->lmpassword = *user_info->password.hash.lanman; |
140 | 0 | password_info->ntpassword = *user_info->password.hash.nt; |
141 | |
|
142 | 0 | identity_info = &password_info->identity_info; |
143 | 0 | state->req.in.logon_level = 1; |
144 | 0 | state->req.in.logon.password= password_info; |
145 | 0 | } else { |
146 | 0 | struct netr_NetworkInfo *network_info; |
147 | 0 | uint8_t chal[8]; |
148 | |
|
149 | 0 | status = encrypt_user_info(state, ctx->auth_ctx, AUTH_PASSWORD_RESPONSE, |
150 | 0 | user_info, &user_info_new); |
151 | 0 | if (tevent_req_nterror(req, status)) { |
152 | 0 | return tevent_req_post(req, ev); |
153 | 0 | } |
154 | 0 | user_info = user_info_new; |
155 | |
|
156 | 0 | network_info = talloc_zero(state, struct netr_NetworkInfo); |
157 | 0 | if (tevent_req_nomem(network_info, req)) { |
158 | 0 | return tevent_req_post(req, ev); |
159 | 0 | } |
160 | | |
161 | 0 | status = auth_get_challenge(ctx->auth_ctx, chal); |
162 | 0 | if (tevent_req_nterror(req, status)) { |
163 | 0 | return tevent_req_post(req, ev); |
164 | 0 | } |
165 | | |
166 | 0 | memcpy(network_info->challenge, chal, sizeof(network_info->challenge)); |
167 | |
|
168 | 0 | network_info->nt.length = user_info->password.response.nt.length; |
169 | 0 | network_info->nt.data = user_info->password.response.nt.data; |
170 | |
|
171 | 0 | network_info->lm.length = user_info->password.response.lanman.length; |
172 | 0 | network_info->lm.data = user_info->password.response.lanman.data; |
173 | |
|
174 | 0 | identity_info = &network_info->identity_info; |
175 | 0 | state->req.in.logon_level = 2; |
176 | 0 | state->req.in.logon.network = network_info; |
177 | 0 | } |
178 | | |
179 | 0 | identity_info->domain_name.string = user_info->client.domain_name; |
180 | 0 | identity_info->parameter_control = user_info->logon_parameters; /* see MSV1_0_* */ |
181 | 0 | identity_info->logon_id = user_info->logon_id; |
182 | 0 | identity_info->account_name.string = user_info->client.account_name; |
183 | 0 | identity_info->workstation.string = user_info->workstation_name; |
184 | |
|
185 | 0 | state->req.in.validation_level = 6; |
186 | |
|
187 | 0 | subreq = dcerpc_winbind_SamLogon_r_send(state, ev, irpc_handle, |
188 | 0 | &state->req); |
189 | 0 | if (tevent_req_nomem(subreq, req)) { |
190 | 0 | return tevent_req_post(req, ev); |
191 | 0 | } |
192 | 0 | tevent_req_set_callback(subreq, |
193 | 0 | winbind_check_password_done, |
194 | 0 | req); |
195 | |
|
196 | 0 | return req; |
197 | 0 | } |
198 | | |
199 | | static void winbind_check_password_done(struct tevent_req *subreq) |
200 | 0 | { |
201 | 0 | struct tevent_req *req = |
202 | 0 | tevent_req_callback_data(subreq, |
203 | 0 | struct tevent_req); |
204 | 0 | struct winbind_check_password_state *state = |
205 | 0 | tevent_req_data(req, |
206 | 0 | struct winbind_check_password_state); |
207 | 0 | struct auth_method_context *ctx = state->ctx; |
208 | 0 | const struct auth_usersupplied_info *user_info = state->user_info; |
209 | 0 | struct ldb_dn *domain_dn = NULL; |
210 | 0 | const char *nt4_domain = NULL; |
211 | 0 | const char *nt4_account = NULL; |
212 | 0 | struct ldb_message *msg = NULL; |
213 | 0 | NTSTATUS status; |
214 | |
|
215 | 0 | status = dcerpc_winbind_SamLogon_r_recv(subreq, state); |
216 | 0 | if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) { |
217 | 0 | status = NT_STATUS_NO_LOGON_SERVERS; |
218 | 0 | } |
219 | 0 | TALLOC_FREE(subreq); |
220 | 0 | if (tevent_req_nterror(req, status)) { |
221 | 0 | return; |
222 | 0 | } |
223 | | |
224 | 0 | status = state->req.out.result; |
225 | 0 | if (!NT_STATUS_IS_OK(status)) { |
226 | 0 | if (!state->req.out.authoritative) { |
227 | 0 | state->authoritative = false; |
228 | 0 | } |
229 | 0 | tevent_req_nterror(req, status); |
230 | 0 | return; |
231 | 0 | } |
232 | | |
233 | 0 | status = make_user_info_dc_netlogon_validation(state, |
234 | 0 | user_info->client.account_name, |
235 | 0 | state->req.in.validation_level, |
236 | 0 | &state->req.out.validation, |
237 | 0 | true, /* This user was authenticated */ |
238 | 0 | &state->user_info_dc); |
239 | 0 | if (tevent_req_nterror(req, status)) { |
240 | 0 | return; |
241 | 0 | } |
242 | | |
243 | 0 | nt4_domain = state->user_info_dc->info->domain_name; |
244 | 0 | nt4_account = state->user_info_dc->info->account_name; |
245 | |
|
246 | 0 | if (lpcfg_is_mydomain(ctx->auth_ctx->lp_ctx, nt4_domain)) { |
247 | 0 | domain_dn = ldb_get_default_basedn(ctx->auth_ctx->sam_ctx); |
248 | 0 | } |
249 | |
|
250 | 0 | if (domain_dn != NULL) { |
251 | | /* |
252 | | * At best, reset the badPwdCount to 0 if the account exists. |
253 | | * This means that lockouts happen at a badPwdCount earlier than |
254 | | * normal, but makes it more fault tolerant. |
255 | | */ |
256 | 0 | status = authsam_search_account(state, ctx->auth_ctx->sam_ctx, |
257 | 0 | nt4_account, domain_dn, &msg); |
258 | 0 | if (NT_STATUS_IS_OK(status)) { |
259 | 0 | status = authsam_logon_success_accounting( |
260 | 0 | ctx->auth_ctx->sam_ctx, msg, |
261 | 0 | domain_dn, |
262 | 0 | user_info->flags & USER_INFO_INTERACTIVE_LOGON, |
263 | 0 | NULL, NULL); |
264 | 0 | if (tevent_req_nterror(req, status)) { |
265 | 0 | return; |
266 | 0 | } |
267 | 0 | } |
268 | 0 | } |
269 | | |
270 | | /* |
271 | | * We need to expand group memberships within our local domain, |
272 | | * as the token might be generated by a trusted domain, unless we're |
273 | | * an RODC. |
274 | | */ |
275 | 0 | status = authsam_update_user_info_dc(state->user_info_dc, |
276 | 0 | ctx->auth_ctx->sam_ctx, |
277 | 0 | state->user_info_dc); |
278 | 0 | if (tevent_req_nterror(req, status)) { |
279 | 0 | return; |
280 | 0 | } |
281 | | |
282 | 0 | tevent_req_done(req); |
283 | 0 | } |
284 | | |
285 | | static NTSTATUS winbind_check_password_recv(struct tevent_req *req, |
286 | | TALLOC_CTX *mem_ctx, |
287 | | struct auth_user_info_dc **user_info_dc, |
288 | | const struct authn_audit_info **client_audit_info, |
289 | | const struct authn_audit_info **server_audit_info, |
290 | | bool *pauthoritative) |
291 | 0 | { |
292 | 0 | struct winbind_check_password_state *state = |
293 | 0 | tevent_req_data(req, |
294 | 0 | struct winbind_check_password_state); |
295 | 0 | NTSTATUS status = NT_STATUS_OK; |
296 | |
|
297 | 0 | *pauthoritative = state->authoritative; |
298 | 0 | *client_audit_info = NULL; |
299 | 0 | *server_audit_info = NULL; |
300 | |
|
301 | 0 | if (tevent_req_is_nterror(req, &status)) { |
302 | 0 | tevent_req_received(req); |
303 | 0 | return status; |
304 | 0 | } |
305 | | |
306 | 0 | *user_info_dc = talloc_move(mem_ctx, &state->user_info_dc); |
307 | |
|
308 | 0 | tevent_req_received(req); |
309 | 0 | return NT_STATUS_OK; |
310 | 0 | } |
311 | | |
312 | | static const struct auth_operations winbind_ops = { |
313 | | .name = "winbind", |
314 | | .want_check = winbind_want_check, |
315 | | .check_password_send = winbind_check_password_send, |
316 | | .check_password_recv = winbind_check_password_recv |
317 | | }; |
318 | | |
319 | | _PUBLIC_ NTSTATUS auth4_winbind_init(TALLOC_CTX *ctx) |
320 | 0 | { |
321 | 0 | NTSTATUS ret; |
322 | |
|
323 | 0 | ret = auth_register(ctx, &winbind_ops); |
324 | 0 | if (!NT_STATUS_IS_OK(ret)) { |
325 | 0 | DEBUG(0,("Failed to register 'winbind' auth backend!\n")); |
326 | 0 | return ret; |
327 | 0 | } |
328 | | |
329 | 0 | return NT_STATUS_OK; |
330 | 0 | } |