1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
24  * Copyright 2013 Nexenta Systems, Inc.  All rights reserved.
25  */
26 
27 #include <smbsrv/smb_kproto.h>
28 #include <smbsrv/smb_fsops.h>
29 
30 /*
31  * smb_nt_transact_query_quota
32  *
33  * This method allows the client to retrieve quota information from
34  * the server. The result of the call is returned to the client in the
35  * Data part of the transaction response.
36  *
37  * On entry, the 'TotalParameterCount' field must be equal to 16, and the
38  * client parameter block must be encoded with the following parameters:
39  *
40  * Request                    Description
41  * ========================== ==================================
42  * WORD fid                   SMB file identifier of the target directory
43  * BYTE ReturnSingleEntry     A boolean indicating whether to return
44  *                            a single entry (TRUE) or multiple entries (FALSE).
45  * BYTE RestartScan           A boolean indicating whether to continue from
46  *                            the previous request (FALSE) or restart a new
47  *                            sequence (TRUE).
48  * DWORD SidListLength        The length, in bytes, of the SidList in the
49  *                            data block or 0 if there is no SidList.
50  * DWORD StartSidLength       If SidListLength is 0 (i.e. there is no SidList
51  *                            in the data block), then this is either:
52  *                                 1) the (non-zero) length in bytes of the
53  *                                    StartSid in the parameter buffer, or
54  *                                 2) if 0, there is no StartSid in the
55  *                                    parameter buffer, in which case, all SIDs
56  *                                    are to be enumerated as if they were
57  *                                    passed in the SidList.
58  *                            Otherwise, StartSidLength is ignored.
59  * DWORD StartSidOffset       The offset, in bytes, to the StartSid in the
60  *                            parameter block (if one exists).
61  *
62  * One of SidListLength and StartSidLength must be 0.
63  *
64  * An SMB_COM_NT_TRANSACTION response is sent in reply when the request
65  * is successful.  The 'TotalParameterCount' is set to 4, and the parameter
66  * block in the server response contains a 32-bit unsigned integer
67  * indicating the length, in bytes, of the returned quota information.
68  * The 'TotalDataCount' is set to indicate the length of the data buffer,
69  * and the data buffer contains the following quota information:
70  *
71  *  Data Block Encoding                Description
72  *  ================================== =================================
73  *  ULONG NextEntryOffset;             Offset to start of next entry from
74  *                                     start of this entry, or 0 for the
75  *                                     final entry
76  *  ULONG SidLength;                   Length (bytes) of SID
77  *  SMB_TIME ChangeTime;               Time that the quota was last changed
78  *  LARGE_INTEGER QuotaUsed;           Amount of quota (bytes) used by user
79  *  LARGE_INTEGER QuotaThreshold;      Quota warning limit (bytes) for user
80  *  LARGE_INTEGER QuotaLimit;          The quota limit (bytes) for this user
81  *  USHORT Sid;                        Search handle
82  */
83 smb_sdrc_t
smb_nt_transact_query_quota(smb_request_t * sr,smb_xa_t * xa)84 smb_nt_transact_query_quota(smb_request_t *sr, smb_xa_t *xa)
85 {
86 	uint8_t		single, restart;
87 	uint32_t	sidlistlen, startsidlen, startsidoff;
88 	smb_node_t	*tnode;
89 	smb_ofile_t	*ofile;
90 	smb_quota_query_t request;
91 	smb_quota_response_t reply;
92 	uint32_t status = NT_STATUS_SUCCESS;
93 
94 	bzero(&request, sizeof (smb_quota_query_t));
95 	bzero(&reply, sizeof (smb_quota_response_t));
96 
97 	if (!smb_tree_has_feature(sr->tid_tree, SMB_TREE_QUOTA)) {
98 		smbsr_error(sr, NT_STATUS_NOT_SUPPORTED, 0, 0);
99 		return (SDRC_ERROR);
100 	}
101 
102 	if (xa->smb_tpscnt != 16) {
103 		smbsr_error(sr, NT_STATUS_INVALID_PARAMETER, 0, 0);
104 		return (SDRC_ERROR);
105 	}
106 
107 	if (smb_mbc_decodef(&xa->req_param_mb, "%wbblll", sr, &sr->smb_fid,
108 	    &single, &restart, &sidlistlen, &startsidlen, &startsidoff)) {
109 		smbsr_error(sr, NT_STATUS_INVALID_PARAMETER, 0, 0);
110 		return (SDRC_ERROR);
111 	}
112 
113 	if ((sidlistlen != 0) && (startsidlen != 0)) {
114 		smbsr_error(sr, NT_STATUS_INVALID_PARAMETER, 0, 0);
115 		return (SDRC_ERROR);
116 	}
117 
118 	smbsr_lookup_file(sr);
119 	ofile = sr->fid_ofile;
120 	if (ofile == NULL) {
121 		smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
122 		return (SDRC_ERROR);
123 	}
124 
125 	if ((ofile->f_node == NULL) || (ofile->f_ftype != SMB_FTYPE_DISK)) {
126 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS,
127 		    ERROR_ACCESS_DENIED);
128 		smbsr_release_file(sr);
129 		return (SDRC_ERROR);
130 	}
131 
132 	tnode = sr->tid_tree->t_snode;
133 	request.qq_root_path = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
134 	if (smb_node_getmntpath(tnode, request.qq_root_path, MAXPATHLEN) != 0) {
135 		smbsr_error(sr, NT_STATUS_INVALID_PARAMETER, ERRDOS,
136 		    ERROR_INVALID_PARAMETER);
137 		smbsr_release_file(sr);
138 		kmem_free(request.qq_root_path, MAXPATHLEN);
139 		return (SDRC_ERROR);
140 	}
141 
142 	if (sidlistlen != 0)
143 		request.qq_query_op = SMB_QUOTA_QUERY_SIDLIST;
144 	else if (startsidlen != 0)
145 		request.qq_query_op = SMB_QUOTA_QUERY_STARTSID;
146 	else
147 		request.qq_query_op = SMB_QUOTA_QUERY_ALL;
148 
149 	request.qq_single = single;
150 	request.qq_restart = restart;
151 	smb_quota_max_quota(&xa->rep_data_mb, &request);
152 
153 	status = smb_quota_init_sids(&xa->req_data_mb, &request, ofile);
154 
155 	if (status == NT_STATUS_SUCCESS) {
156 		if (smb_quota_query(sr->sr_server, &request, &reply) != 0) {
157 			status = NT_STATUS_INTERNAL_ERROR;
158 		} else {
159 			status = reply.qr_status;
160 			if (status == NT_STATUS_SUCCESS) {
161 				status = smb_quota_encode_quotas(
162 				    &xa->rep_data_mb,
163 				    &request, &reply, ofile);
164 			}
165 			(void) smb_mbc_encodef(&xa->rep_param_mb, "l",
166 			    xa->rep_data_mb.chain_offset);
167 			xdr_free(smb_quota_response_xdr, (char *)&reply);
168 		}
169 	}
170 
171 	kmem_free(request.qq_root_path, MAXPATHLEN);
172 	smb_quota_free_sids(&request);
173 
174 	if (status != NT_STATUS_SUCCESS) {
175 		if (status == NT_STATUS_NO_MORE_ENTRIES) {
176 			smb_ofile_set_quota_resume(ofile, NULL);
177 			smbsr_warn(sr, status, 0, 0);
178 			status = NT_STATUS_SUCCESS;
179 		} else {
180 			smbsr_error(sr, status, 0, 0);
181 		}
182 		(void) smb_mbc_encodef(&xa->rep_param_mb, "l", 0);
183 	}
184 
185 	smbsr_release_file(sr);
186 	return ((status == NT_STATUS_SUCCESS) ? SDRC_SUCCESS : SDRC_ERROR);
187 }
188 
189 /*
190  * smb_nt_transact_set_quota
191  *
192  * This method allows the client to set quota information on the server.
193  * The result status of the call is returned to the client in the
194  * 'status' field of the SMB response header.
195  *
196  * On entry, the 'TotalParameterCount' field must be equal to 2, and the
197  * client parameter block must be encoded with the following parameters:
198  *
199  *  Data Block Encoding                Description
200  *  ================================== =================================
201  *  ULONG NextEntryOffset;             Offset to start of next entry from
202  *                                     start of this entry, or 0 for the
203  *                                     final entry
204  *  ULONG SidLength;                   Length (bytes) of SID
205  *  SMB_TIME ChangeTime;               Time that the quota was last changed
206  *  LARGE_INTEGER QuotaUsed;           Amount of quota (bytes) used by user
207  *  LARGE_INTEGER QuotaThreshold;      Quota warning limit (bytes) for user
208  *  LARGE_INTEGER QuotaLimit;          The quota limit (bytes) for this user
209  *  VARIABLE Sid;                      Security identifier of the user
210  *
211  * An SMB_COM_NT_TRANSACTION response is sent in reply when the request
212  * is successful.  The 'TotalParameterCount' and the 'TotalDataCount' are set
213  * to 0, and the parameter block 'Status' field in the server SMB response
214  * header contains a 32-bit unsigned integer indicating the result status
215  * (NT_STATUS_SUCCESS if successful).
216  *
217  * Only users with Admin privileges (i.e. of the BUILTIN/Administrators
218  * group) will be allowed to set quotas.
219  */
220 smb_sdrc_t
smb_nt_transact_set_quota(smb_request_t * sr,smb_xa_t * xa)221 smb_nt_transact_set_quota(smb_request_t *sr, smb_xa_t *xa)
222 {
223 	char		*root_path;
224 	uint32_t	status = NT_STATUS_SUCCESS;
225 	smb_node_t	*tnode;
226 	smb_ofile_t	*ofile;
227 	smb_quota_set_t request;
228 	uint32_t	reply;
229 	list_t 		*quota_list;
230 
231 	bzero(&request, sizeof (smb_quota_set_t));
232 
233 	if (!smb_tree_has_feature(sr->tid_tree, SMB_TREE_QUOTA)) {
234 		smbsr_error(sr, NT_STATUS_NOT_SUPPORTED, 0, 0);
235 		return (SDRC_ERROR);
236 	}
237 
238 	if (!smb_user_is_admin(sr->uid_user)) {
239 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRSRV, ERRaccess);
240 		return (-1);
241 	}
242 
243 	if (xa->smb_tpscnt != 2) {
244 		smbsr_error(sr, NT_STATUS_INVALID_PARAMETER, 0, 0);
245 		return (SDRC_ERROR);
246 	}
247 
248 	if (smb_mbc_decodef(&xa->req_param_mb, "%w", sr,
249 	    &sr->smb_fid)) {
250 		smbsr_error(sr, NT_STATUS_INVALID_PARAMETER, 0, 0);
251 		return (SDRC_ERROR);
252 	}
253 
254 	smbsr_lookup_file(sr);
255 	ofile = sr->fid_ofile;
256 	if (ofile == NULL) {
257 		smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
258 		return (SDRC_ERROR);
259 	}
260 
261 	if ((ofile->f_node == NULL) || (ofile->f_ftype != SMB_FTYPE_DISK)) {
262 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS,
263 		    ERROR_ACCESS_DENIED);
264 		smbsr_release_file(sr);
265 		return (SDRC_ERROR);
266 	}
267 
268 	tnode = sr->tid_tree->t_snode;
269 	root_path  = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
270 	if (smb_node_getmntpath(tnode, root_path, MAXPATHLEN) != 0) {
271 		smbsr_error(sr, NT_STATUS_INVALID_PARAMETER, ERRDOS,
272 		    ERROR_INVALID_PARAMETER);
273 		smbsr_release_file(sr);
274 		kmem_free(root_path, MAXPATHLEN);
275 		return (SDRC_ERROR);
276 	}
277 
278 	quota_list = &request.qs_quota_list;
279 	list_create(quota_list, sizeof (smb_quota_t),
280 	    offsetof(smb_quota_t, q_list_node));
281 
282 	status = smb_quota_decode_quotas(&xa->req_data_mb, quota_list);
283 	if (status == NT_STATUS_SUCCESS) {
284 		request.qs_root_path = root_path;
285 		if (smb_quota_set(sr->sr_server, &request, &reply) != 0) {
286 			status = NT_STATUS_INTERNAL_ERROR;
287 		} else {
288 			status = reply;
289 			xdr_free(xdr_uint32_t, (char *)&reply);
290 		}
291 	}
292 
293 	kmem_free(root_path, MAXPATHLEN);
294 	smb_quota_free_quotas(&request.qs_quota_list);
295 	smbsr_release_file(sr);
296 
297 	if (status != NT_STATUS_SUCCESS) {
298 		smbsr_error(sr, status, 0, 0);
299 		(void) smb_mbc_encodef(&xa->rep_param_mb, "l", 0);
300 		return (SDRC_ERROR);
301 	}
302 
303 	return (SDRC_SUCCESS);
304 }
305