1/*
2 * Copyright (c) 2000, Boris Popov
3 * All rights reserved.
4 *
5 * Portions Copyright (C) 2001 - 2010 Apple Inc. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 *    must display the following acknowledgement:
17 *    This product includes software developed by Boris Popov.
18 * 4. Neither the name of the author nor the names of any co-contributors
19 *    may be used to endorse or promote products derived from this software
20 *    without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 *
34 * $Id: rap.c,v 1.6 2005/05/06 23:16:29 lindak Exp $
35 *
36 * This is very simple implementation of RAP protocol.
37 */
38#include <sys/param.h>
39#include <sys/errno.h>
40#include <sys/stat.h>
41#include <ctype.h>
42#include <err.h>
43#include <stdio.h>
44#include <unistd.h>
45#include <strings.h>
46#include <stdlib.h>
47#include <sysexits.h>
48
49#include <stdint.h>
50#include <libkern/OSByteOrder.h>
51#include <smbclient/smbclient.h>
52#include <smbclient/smbclient_internal.h>
53#include <smbclient/ntstatus.h>
54
55#include "rap.h"
56
57#define MAX_RAP_SHARE_BUFFER 0xffe0
58
59static int
60smb_rap_parserqparam(const char *s, char **next, int *rlen)
61{
62	char *np;
63	int len;
64
65	switch (*s++) {
66	    case 'L':
67	    case 'T':
68	    case 'W':
69		len = 2;
70		break;
71	    case 'D':
72	    case 'O':
73		len = 4;
74		break;
75	    case 'b':
76	    case 'F':
77		len = 1;
78		break;
79	    case 'r':
80	    case 's':
81		len = 0;
82		break;
83	    default:
84		return EINVAL;
85	}
86	if (isdigit(*s)) {
87		len *= (int)strtoul(s, &np, 10);
88		s = np;
89	}
90	*rlen = len;
91	*(const char**)next = s;
92	return 0;
93}
94
95static int
96smb_rap_parserpparam(const char *s, char **next, int *rlen)
97{
98	char *np;
99	int len;
100
101	switch (*s++) {
102	    case 'e':
103	    case 'h':
104		len = 2;
105		break;
106	    case 'i':
107		len = 4;
108		break;
109	    case 'g':
110		len = 1;
111		break;
112	    default:
113		return EINVAL;
114	}
115	if (isdigit(*s)) {
116		len *= (int)strtoul(s, &np, 10);
117		s = np;
118	}
119	*rlen = len;
120	*(const char**)next = s;
121	return 0;
122}
123
124static int
125smb_rap_parserpdata(const char *s, char **next, int *rlen)
126{
127	char *np;
128	int len;
129
130	switch (*s++) {
131	    case 'B':
132		len = 1;
133		break;
134	    case 'W':
135		len = 2;
136		break;
137	    case 'D':
138	    case 'O':
139	    case 'z':
140		len = 4;
141		break;
142	    default:
143		return EINVAL;
144	}
145	if (isdigit(*s)) {
146		len *= (int)strtoul(s, &np, 10);
147		s = np;
148	}
149	*rlen = len;
150	*(const char**)next = s;
151	return 0;
152}
153
154static int
155smb_rap_rqparam_z(struct smb_rap *rap, const char *value)
156{
157	int len = (int)strlen(value) + 1;
158
159	bcopy(value, rap->r_npbuf, len);
160	rap->r_npbuf += len;
161	rap->r_plen += len;
162	return 0;
163}
164
165static int
166smb_rap_rqparam(struct smb_rap *rap, char ptype, char plen, int32_t value)
167{
168	char *p = rap->r_npbuf;
169	int len;
170
171	switch (ptype) {
172		case 'L':
173		case 'W':
174				setwle(p, 0, value);
175				len = 2;
176				break;
177		case 'D':
178				setdle(p, 0, value);
179				len = 4;
180				break;
181		case 'b':
182				memset(p, value, plen);
183				len = plen;
184				break;
185		default:
186				return EINVAL;
187	}
188	rap->r_npbuf += len;
189	rap->r_plen += len;
190	return 0;
191}
192
193static int
194smb_rap_create(int fn, const char *param, const char *data,
195	struct smb_rap **rapp)
196{
197	struct smb_rap *rap;
198	char *p;
199	int plen, len;
200
201	rap = malloc(sizeof(*rap));
202	if (rap == NULL)
203		return ENOMEM;
204	bzero(rap, sizeof(*rap));
205	p = rap->r_sparam = rap->r_nparam = strdup(param);
206	rap->r_sdata = rap->r_ndata = strdup(data);
207	/*
208	 * Calculate length of request parameter block
209	 */
210	len = 2 + (int)strlen(param) + 1 + (int)strlen(data) + 1;
211
212	while (*p) {
213		if (smb_rap_parserqparam(p, &p, &plen) != 0)
214			break;
215		len += plen;
216	}
217	rap->r_pbuf = rap->r_npbuf = malloc(len);
218	smb_rap_rqparam(rap, 'W', 1, fn);
219	smb_rap_rqparam_z(rap, rap->r_sparam);
220	smb_rap_rqparam_z(rap, rap->r_sdata);
221	*rapp = rap;
222	return 0;
223}
224
225static void
226smb_rap_done(struct smb_rap *rap)
227{
228	if (rap->r_sparam)
229		free(rap->r_sparam);
230	if (rap->r_sdata)
231		free(rap->r_sdata);
232	if (rap->r_pbuf)
233		free(rap->r_pbuf);
234	if (rap->r_dbuf)
235		free(rap->r_dbuf);
236	free(rap);
237}
238
239static int
240smb_rap_setNparam(struct smb_rap *rap, int32_t value)
241{
242	char *p = rap->r_nparam;
243	char ptype = *p;
244	int error, plen;
245
246	error = smb_rap_parserqparam(p, &p, &plen);
247	if (error)
248		return error;
249	switch (ptype) {
250	    case 'L':
251			rap->r_rcvbuflen = value;
252			/* FALLTHROUGH */
253	    case 'W':
254	    case 'D':
255	    case 'b':
256			error = smb_rap_rqparam(rap, ptype, plen, value);
257		break;
258	    default:
259			return EINVAL;
260	}
261	rap->r_nparam = p;
262	return error;
263}
264
265static int
266smb_rap_setPparam(struct smb_rap *rap, void *value)
267{
268	char *p = rap->r_nparam;
269	char ptype = *p;
270	int error, plen;
271
272	error = smb_rap_parserqparam(p, &p, &plen);
273	if (error)
274		return error;
275	switch (ptype) {
276	    case 'r':
277		rap->r_rcvbuf = value;
278		break;
279	    default:
280		return EINVAL;
281	}
282	rap->r_nparam = p;
283	return 0;
284}
285
286static int
287smb_rap_getNparam(struct smb_rap *rap, int32_t *value)
288{
289	char *p = rap->r_nparam;
290	char ptype = *p;
291	int error, plen;
292
293	error = smb_rap_parserpparam(p, &p, &plen);
294	if (error)
295		return error;
296	switch (ptype) {
297	    case 'h':
298		*value = OSSwapLittleToHostInt16(*(uint16_t*)rap->r_npbuf);
299		break;
300	    default:
301		return EINVAL;
302	}
303	rap->r_npbuf += plen;
304	rap->r_nparam = p;
305	return 0;
306}
307
308static int
309smb_rap_request(SMBHANDLE inConnection, struct smb_rap *rap)
310{
311	uint16_t *rp, conv;
312	uint32_t *p32;
313	char *dp, *p = rap->r_nparam;
314	char ptype;
315	int error = 0, entries, done, dlen, status;
316	size_t rdatacnt, rparamcnt;
317
318	rdatacnt = rap->r_rcvbuflen;
319	rparamcnt = rap->r_plen;
320
321	status = SMBTransactMailSlot(inConnection, "\\PIPE\\LANMAN",
322								 rap->r_pbuf, rap->r_plen,
323								 rap->r_pbuf, &rparamcnt,
324								 rap->r_rcvbuf, &rdatacnt);
325	if (!NT_SUCCESS(status)) {
326		/* Should never happen */
327		return errno;
328	}
329	rp = (uint16_t*)rap->r_pbuf;
330	rap->r_result = OSSwapLittleToHostInt16(*rp++);
331	conv = OSSwapLittleToHostInt16(*rp++);
332	rap->r_npbuf = (char*)rp;
333	rap->r_entries = entries = 0;
334	done = 0;
335	while (!done && *p) {
336		ptype = *p;
337		switch (ptype) {
338		    case 'e':
339			rap->r_entries = entries = OSSwapLittleToHostInt16(*(uint16_t*)rap->r_npbuf);
340			rap->r_npbuf += 2;
341			p++;
342			break;
343		    default:
344			done = 1;
345		}
346	}
347	rap->r_nparam = p;
348	/*
349	 * In general, unpacking entries we may need to relocate
350	 * entries for proper aligning. For now use them as is.
351	 */
352	dp = rap->r_rcvbuf;
353	while (entries--) {
354		p = rap->r_sdata;
355		while (*p) {
356			ptype = *p;
357			error = smb_rap_parserpdata(p, &p, &dlen);
358			if (error) {
359				SMBLogInfo("reply data mismatch %s", ASL_LEVEL_ERR, p);
360				return EBADRPC;
361			}
362			switch (ptype) {
363			    case 'z':
364				p32 = (uint32_t*)dp;
365				*p32 = (OSSwapLittleToHostInt32(*p32) & 0xffff) - conv;
366				break;
367			}
368			dp += dlen;
369		}
370	}
371	return error;
372}
373
374/*
375 * We could translate these better, but these are the old enumerate rap calls and
376 * we are using them less and less. The old code just passed up the rap errors, now
377 * we convert it to real errno.
378 */
379static int smb_rap_error(struct smb_rap *rap, int error)
380{
381	if (error)
382		return error;
383	if ((rap->r_result == 0) || (rap->r_result == SMB_ERROR_MORE_DATA))
384		return 0;
385	switch (rap->r_result) {
386	case SMB_ERROR_ACCESS_DENIED:
387	case SMB_ERROR_NETWORK_ACCESS_DENIED:
388		error = EACCES;
389		break;
390	default:
391		SMBLogInfo("received an unknown rap enumerate share error %d",
392					 ASL_LEVEL_ERR, rap->r_result);
393		error = EIO;
394		break;
395	}
396	return error;
397}
398
399int
400RapNetShareEnum(SMBHANDLE inConnection, int sLevel, void **rBuffer, uint32_t *rBufferSize,
401				uint32_t *entriesRead, uint32_t *totalEntriesRead)
402{
403	struct smb_rap *rap = NULL;
404	int error;
405
406	if (inConnection == NULL) {
407		/* Should never happen */
408		return EINVAL;
409	}
410	*rBufferSize = MAX_RAP_SHARE_BUFFER;	/* samba notes win2k bug for 65535 */
411	*rBuffer = malloc(*rBufferSize);
412	if (*rBuffer == NULL) {
413		return errno;
414	}
415
416	error = smb_rap_create(0, "WrLeh", "B13BWz", &rap);
417	if (error) {
418		RapNetApiBufferFree(*rBuffer);
419		*rBuffer = NULL;
420		return error;
421	}
422	smb_rap_setNparam(rap, sLevel);		/* W - sLevel */
423	smb_rap_setPparam(rap, *rBuffer);	/* r - pbBuffer */
424	smb_rap_setNparam(rap, *rBufferSize);	/* L - cbBuffer */
425	error = smb_rap_request(inConnection, rap);
426
427	if (error == 0) {
428		*entriesRead = rap->r_entries;
429		if (totalEntriesRead) {
430			int32_t lval = 0;
431			error = smb_rap_getNparam(rap, &lval);
432			*totalEntriesRead = lval;
433		}
434	}
435	error = smb_rap_error(rap, error);
436	smb_rap_done(rap);
437	return error;
438}
439
440void RapNetApiBufferFree(void * bufptr)
441{
442	free(bufptr);
443}
444