1/*
2   Unix SMB/CIFS implementation.
3
4   Winbind authentication mechnism
5
6   Copyright (C) Tim Potter 2000
7   Copyright (C) Andrew Bartlett 2001 - 2002
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#undef DBGC_CLASS
27#define DBGC_CLASS DBGC_AUTH
28
29static NTSTATUS get_info3_from_ndr(TALLOC_CTX *mem_ctx, struct winbindd_response *response, NET_USER_INFO_3 *info3)
30{
31	uint8 *info3_ndr;
32	size_t len = response->length - sizeof(struct winbindd_response);
33	prs_struct ps;
34	if (len > 0) {
35		info3_ndr = response->extra_data;
36		if (!prs_init(&ps, len, mem_ctx, UNMARSHALL)) {
37			return NT_STATUS_NO_MEMORY;
38		}
39		prs_copy_data_in(&ps, (char *)info3_ndr, len);
40		prs_set_offset(&ps,0);
41		if (!net_io_user_info3("", info3, &ps, 1, 3)) {
42			DEBUG(2, ("get_info3_from_ndr: could not parse info3 struct!\n"));
43			return NT_STATUS_UNSUCCESSFUL;
44		}
45		prs_mem_free(&ps);
46
47		return NT_STATUS_OK;
48	} else {
49		DEBUG(2, ("get_info3_from_ndr: No info3 struct found!\n"));
50		return NT_STATUS_UNSUCCESSFUL;
51	}
52}
53
54/* Authenticate a user with a challenge/response */
55
56static NTSTATUS check_winbind_security(const struct auth_context *auth_context,
57				       void *my_private_data,
58				       TALLOC_CTX *mem_ctx,
59				       const auth_usersupplied_info *user_info,
60				       auth_serversupplied_info **server_info)
61{
62	struct winbindd_request request;
63	struct winbindd_response response;
64        NSS_STATUS result;
65	NTSTATUS nt_status;
66        NET_USER_INFO_3 info3;
67
68	if (!user_info) {
69		return NT_STATUS_INVALID_PARAMETER;
70	}
71
72	if (!auth_context) {
73		DEBUG(3,("Password for user %s cannot be checked because we have no auth_info to get the challenge from.\n",
74			 user_info->internal_username.str));
75		return NT_STATUS_INVALID_PARAMETER;
76	}
77
78	if (strequal(user_info->domain.str, get_global_sam_name())) {
79		DEBUG(3,("check_winbind_security: Not using winbind, requested domain [%s] was for this SAM.\n",
80			user_info->domain.str));
81		return NT_STATUS_NOT_IMPLEMENTED;
82	}
83
84	/* Send off request */
85
86	ZERO_STRUCT(request);
87	ZERO_STRUCT(response);
88
89	request.flags = WBFLAG_PAM_INFO3_NDR;
90
91	fstrcpy(request.data.auth_crap.user,
92			  user_info->smb_name.str);
93	fstrcpy(request.data.auth_crap.domain,
94			  user_info->domain.str);
95	fstrcpy(request.data.auth_crap.workstation,
96			  user_info->wksta_name.str);
97
98	memcpy(request.data.auth_crap.chal, auth_context->challenge.data, sizeof(request.data.auth_crap.chal));
99
100	request.data.auth_crap.lm_resp_len = MIN(user_info->lm_resp.length,
101						 sizeof(request.data.auth_crap.lm_resp));
102	request.data.auth_crap.nt_resp_len = MIN(user_info->nt_resp.length,
103						 sizeof(request.data.auth_crap.nt_resp));
104
105	memcpy(request.data.auth_crap.lm_resp, user_info->lm_resp.data,
106	       request.data.auth_crap.lm_resp_len);
107	memcpy(request.data.auth_crap.nt_resp, user_info->nt_resp.data,
108	       request.data.auth_crap.nt_resp_len);
109
110	/* we are contacting the privileged pipe */
111	become_root();
112	result = winbindd_request(WINBINDD_PAM_AUTH_CRAP, &request, &response);
113	unbecome_root();
114
115	if ( result == NSS_STATUS_UNAVAIL )  {
116		struct auth_methods *auth_method = my_private_data;
117
118		if ( auth_method )
119			return auth_method->auth(auth_context, auth_method->private_data,
120				mem_ctx, user_info, server_info);
121		else
122			/* log an error since this should not happen */
123			DEBUG(0,("check_winbind_security: ERROR!  my_private_data == NULL!\n"));
124	}
125
126	nt_status = NT_STATUS(response.data.auth.nt_status);
127
128	if (result == NSS_STATUS_SUCCESS && response.extra_data) {
129		if (NT_STATUS_IS_OK(nt_status)) {
130			if (NT_STATUS_IS_OK(nt_status = get_info3_from_ndr(mem_ctx, &response, &info3))) {
131				nt_status = make_server_info_info3(mem_ctx,
132					user_info->internal_username.str,
133					user_info->smb_name.str, user_info->domain.str,
134					server_info, &info3);
135			}
136
137		}
138	} else if (NT_STATUS_IS_OK(nt_status)) {
139		nt_status = NT_STATUS_NO_LOGON_SERVERS;
140	}
141
142	SAFE_FREE(response.extra_data);
143        return nt_status;
144}
145
146/* module initialisation */
147static NTSTATUS auth_init_winbind(struct auth_context *auth_context, const char *param, auth_methods **auth_method)
148{
149	if (!make_auth_methods(auth_context, auth_method)) {
150		return NT_STATUS_NO_MEMORY;
151	}
152
153	(*auth_method)->name = "winbind";
154	(*auth_method)->auth = check_winbind_security;
155
156	if (param && *param) {
157		/* we load the 'fallback' module - if winbind isn't here, call this
158		   module */
159		if (!load_auth_module(auth_context, param, (auth_methods **)&(*auth_method)->private_data)) {
160			return NT_STATUS_UNSUCCESSFUL;
161		}
162
163	}
164	return NT_STATUS_OK;
165}
166
167NTSTATUS auth_winbind_init(void)
168{
169	return smb_register_auth(AUTH_INTERFACE_VERSION, "winbind", auth_init_winbind);
170}
171