1/*
2 * Copyright (c) 2009 - 2010 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24#include "ntstatus.h"
25#include "smbclient.h"
26#include "netbios.h"
27#include "smbclient_internal.h"
28#include "smbclient_private.h"
29
30#include <netsmb/netbios.h>
31#include <netsmb/smb_lib.h>
32#include <netsmb/nb_lib.h>
33#include <netsmb/smb_conn.h>
34#include "charsets.h"
35#include "parse_url.h"
36#include "remount.h"
37#include <netsmb/upi_mbuf.h>
38#include <sys/mchain.h>
39#include "msdfs.h"
40#include <smbfs/smbfs.h>
41
42void SMBLogInfo(const char *fmt, int log_level,...)
43{
44	int save_errno = errno;
45	va_list ap;
46	aslmsg m = asl_new(ASL_TYPE_MSG);
47
48
49	va_start(ap, log_level);
50	asl_vlog(NULL, m, log_level, fmt, ap);
51	va_end(ap);
52	asl_free(m);
53	errno = save_errno; /* Never let this routine change errno */
54}
55
56/* Find all address associated with the NetBIOS name */
57struct sockaddr_storage *
58SMBResolveNetBIOSNameEx(const char *hostName, uint8_t nodeType,
59						 const char *winServer, uint32_t timeout,
60						 struct sockaddr_storage *respAddr, int32_t *outCount)
61{
62	struct sockaddr_storage *outAddr = NULL, *listAddr;
63	char *netbios_name = NULL;
64	struct nb_ctx ctx;
65	CFMutableArrayRef addressArray = NULL;
66	CFMutableDataRef addressData;
67	struct connectAddress *conn;
68	CFIndex ii;
69	int error = 0;
70	struct smb_prefs prefs;
71
72	bzero(&ctx, sizeof(struct nb_ctx));
73	/* Read the preference files */
74	readPreferences(&prefs, NULL, NULL, FALSE, TRUE);
75
76	/* They gave us a wins server use it */
77	if (winServer) {
78		setWINSAddress(&prefs, winServer, 1);
79	}
80	/* They gave us a timeout value use it */
81	if ((int32_t)timeout > 0) {
82		prefs.NetBIOSResolverTimeout = timeout;
83	}
84
85	/*
86	 * We uppercase and convert the server name given in the URL to Windows Code
87	 * Page.
88	 */
89	netbios_name = convert_utf8_to_wincs(hostName, prefs.WinCodePage, TRUE);
90	if (netbios_name == NULL) {
91		error = ENOMEM;
92		goto done;
93	}
94	/* Only returns IPv4 address */
95	error = nbns_resolvename(&ctx, &prefs, netbios_name, nodeType, &addressArray,
96							 SMB_TCP_PORT_445, TRUE, FALSE, NULL);
97	if (error) {
98		goto done;
99	}
100
101	if (respAddr) {
102		memcpy(respAddr, &ctx.nb_sender, sizeof(ctx.nb_sender));
103	}
104
105	listAddr = outAddr = malloc(CFArrayGetCount(addressArray) * sizeof(struct sockaddr_storage));
106	if (outAddr == NULL) {
107		error = ENOMEM;
108		goto done;
109	}
110
111	for (ii=0; ii < CFArrayGetCount(addressArray); ii++) {
112		addressData = (CFMutableDataRef)CFArrayGetValueAtIndex(addressArray, ii);
113		if (addressData) {
114			conn = (struct connectAddress *)(void *)CFDataGetMutableBytePtr(addressData);
115			if (conn) {
116				*outCount += 1;
117				*listAddr++ = conn->storage;
118			}
119		}
120	}
121	if (*outCount == 0) {
122		free(outAddr);
123		outAddr = NULL; /* Didn't really find any */
124		error = EHOSTUNREACH;
125	}
126done:
127	if (addressArray)
128		CFRelease(addressArray);
129
130	if (netbios_name) {
131		free(netbios_name);
132	}
133	releasePreferenceInfo(&prefs);
134	errno = error;
135	return outAddr;
136}
137
138
139/* Find all address associated with the NetBIOS name */
140ssize_t SMBResolveNetBIOSName(const char * hostName, uint8_t nodeType,
141							  uint32_t timeout, struct sockaddr_storage ** results)
142{
143	int32_t outCount = 0;
144
145	*results = SMBResolveNetBIOSNameEx(hostName, nodeType, NULL, timeout, NULL, &outCount);
146	/* We didn't time out, report to the calling process that we had an error */
147	if (errno && (errno != EHOSTUNREACH)) {
148		return -1;
149	}
150
151	return outCount;
152}
153
154int
155SMBFrameworkVersion(void) {
156	return SMBFS_VERSION;
157}
158
159/* Convert from UTF8 using system code page */
160char *
161SMBConvertFromUTF8ToCodePage(const char *utf8Str, int uppercase)
162{
163	return convert_utf8_to_wincs(utf8Str, getPrefsCodePage(), uppercase);
164
165}
166
167/* Convert to UTF8 using system code page */
168char *
169SMBConvertFromCodePageToUTF8(const char *cpStr)
170{
171	return convert_wincs_to_utf8(cpStr, getPrefsCodePage());
172
173}
174
175/* Convert UTF16 to UTF8 string */
176char *
177SMBConvertFromUTF16ToUTF8(const uint16_t *utf16str, size_t maxLen, uint64_t options)
178{
179#pragma unused(options)
180	/* XXX - TBD Currently we ignore the options */
181	return convert_unicode_to_utf8(utf16str, maxLen);
182
183}
184
185/* Convert UTF16 to UTF8 string */
186uint16_t *
187SMBConvertFromUTF8ToUTF16(const char *utf8str, size_t maxLen, uint64_t options)
188{
189#pragma unused(maxLen, options)
190	/* XXX - TBD Currently we ignore the length field and options */
191	return convert_utf8_to_leunicode(utf8str);
192
193}
194
195/* Find all the NetBIOS names associated using supplied host name */
196struct NodeStatusInfo *
197SMBGetNodeStatus(const char *hostName, uint32_t *outCount)
198{
199	struct NodeStatusInfo *outAddr = NULL, *listAddr;
200	CFMutableArrayRef addressArray = NULL;
201	CFMutableDataRef theData;
202	CFMutableArrayRef nbrrArray;
203	CFIndex ii;
204	uint32_t jj;
205	struct connectAddress *conn;
206	struct nb_ctx ctx;
207	struct smb_prefs prefs;
208	int error;
209
210	bzero(&ctx, sizeof(struct nb_ctx));
211	/* Read the preference files */
212	readPreferences(&prefs, NULL, NULL, FALSE, TRUE);
213	/* Resolve the host name into a list of address */
214	error = resolvehost(hostName, &addressArray, NULL, NBNS_UDP_PORT_137, TRUE, FALSE);
215	if (error) {
216		goto done;
217	}
218	/* Allocate the memory needed to hold the list of address and info */
219	listAddr = outAddr = malloc(CFArrayGetCount(addressArray) * sizeof(struct NodeStatusInfo));
220	if (outAddr == NULL) {
221		error = ENOMEM;
222		goto done;
223	}
224
225	for (ii=0; ii < CFArrayGetCount(addressArray); ii++) {
226		theData = (CFMutableDataRef)CFArrayGetValueAtIndex(addressArray, ii);
227		if (! theData) {
228			continue;
229		}
230		conn = (struct connectAddress *)(void *)CFDataGetMutableBytePtr(theData);
231		if (!conn) {
232			continue;
233		}
234		memcpy(&listAddr->node_storage, &conn->storage, sizeof(conn->storage));
235		listAddr->node_servername[0] = (char)0;
236		listAddr->node_workgroupname[0] = (char)0;
237		listAddr->node_nbrrArray = NULL;
238		/* Creating the array causes nbns_getnodestatus to return a full list */
239		nbrrArray = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0,
240													   &kCFTypeArrayCallBacks );
241		listAddr->node_errno = nbns_getnodestatus(&conn->addr, &ctx, &prefs, NULL,
242												  listAddr->node_servername,
243												  listAddr->node_workgroupname,
244												  nbrrArray);
245
246		if (nbrrArray && (listAddr->node_errno == 0)) {
247			struct NBResourceRecord *nbrrDest;
248
249			listAddr->node_nbrrArrayCnt = (uint32_t)CFArrayGetCount(nbrrArray);
250			if (listAddr->node_nbrrArrayCnt) {
251				listAddr->node_nbrrArray = malloc(listAddr->node_nbrrArrayCnt * sizeof(struct NBResourceRecord));
252			}
253			nbrrDest = listAddr->node_nbrrArray;
254			if (listAddr->node_nbrrArray)
255			for (jj=0; jj < listAddr->node_nbrrArrayCnt; jj++) {
256				struct NBResourceRecord *nbrrSrc = NULL;
257
258				theData = (CFMutableDataRef)CFArrayGetValueAtIndex(nbrrArray, jj);
259				if (theData) {
260					nbrrSrc = (struct NBResourceRecord *)(void *)CFDataGetMutableBytePtr(theData);
261				}
262				if (nbrrSrc == NULL) {
263					listAddr->node_errno = ENOMEM;
264					break;
265				}
266				memcpy(nbrrDest, nbrrSrc, sizeof(*nbrrSrc));
267				nbrrDest++;
268			}
269		}
270		if ((listAddr->node_errno) && (listAddr->node_nbrrArray)) {
271			free(listAddr->node_nbrrArray);
272			listAddr->node_nbrrArray = NULL;
273			listAddr->node_nbrrArrayCnt = 0;
274		}
275		/* Done release it */
276		if (nbrrArray) {
277			CFRelease(nbrrArray);
278		}
279		listAddr++;
280		*outCount += 1;
281	}
282
283	if (*outCount == 0) {
284		free(outAddr);
285		outAddr = NULL; /* Didn't really find any */
286		error = EHOSTUNREACH;
287	}
288
289done:
290	if (addressArray) {
291		CFRelease(addressArray);
292	}
293	releasePreferenceInfo(&prefs);
294	errno = error;
295	return outAddr;
296}
297
298int SMBCheckForAlreadyMountedShare(SMBHANDLE inConnection,
299						  CFStringRef shareRef, CFMutableDictionaryRef mdictRef,
300						  struct statfs *fs, int fs_cnt)
301{
302	void * hContext;
303	NTSTATUS status;
304	CFMutableStringRef upperStringRef;
305	char ShareName[SMB_MAXSHARENAMELEN + 1];
306
307	memset(ShareName, 0, sizeof(ShareName));
308	status = SMBServerContext(inConnection, &hContext);
309	if (!NT_SUCCESS(status)) {
310		return EBADF;
311	}
312	upperStringRef = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, shareRef);
313	if (upperStringRef == NULL) {
314		return ENOMEM;
315	}
316	CFStringUppercase(upperStringRef, NULL);
317	CFStringGetCString(upperStringRef, ShareName, SMB_MAXSHARENAMELEN + 1, kCFStringEncodingUTF8);
318	CFRelease(upperStringRef);
319	return already_mounted(hContext, ShareName, fs, fs_cnt, mdictRef, 0);
320}
321
322int SMBSetNetworkIdentity(SMBHANDLE inConnection, void *network_sid, char *account, char *domain)
323{
324	NTSTATUS	status;
325	int			error;
326    void		*hContext = NULL;
327	ntsid_t		*ntsid = (ntsid_t *)network_sid;
328	struct smbioc_ntwrk_identity ntwrkID;
329
330	status = SMBServerContext(inConnection, &hContext);
331	if (!NT_SUCCESS(status)) {
332		/* Couldn't get the context? */
333        return EINVAL;
334    }
335	memset(&ntwrkID, 0, sizeof(ntwrkID));
336	ntwrkID.ioc_version = SMB_IOC_STRUCT_VERSION;
337	ntwrkID.ioc_ntsid_len = sizeof(*ntsid);
338	ntwrkID.ioc_ntsid = *ntsid;
339	if (account) {
340		strlcpy(ntwrkID.ioc_ntwrk_account, account, sizeof(ntwrkID.ioc_ntwrk_account));
341	}
342	if (domain) {
343		strlcpy(ntwrkID.ioc_ntwrk_domain, domain, sizeof(ntwrkID.ioc_ntwrk_domain));
344	}
345	if (smb_ioctl_call(((struct smb_ctx *)hContext)->ct_fd, SMBIOC_NTWRK_IDENTITY, &ntwrkID) == -1) {
346		error = errno;
347		smb_log_info("The SMBIOC_NTWRK_IDENTITY call failed, syserr = %s",
348					 ASL_LEVEL_DEBUG, strerror(error));
349		return error;
350	}
351	return 0;
352}
353
354char *SMBCreateURLString(const char *domain, const char * user, const char * passwd,
355						 const char *server, const char *path, int32_t port)
356{
357	CFStringRef DomainName = NULL;
358	CFStringRef Username = NULL;
359	CFStringRef Password = NULL;
360	CFStringRef ServerName = NULL;
361	CFStringRef FullPath = NULL;
362	CFStringRef PortNumber = NULL;
363	CFStringRef	URLString = NULL;
364	char *urlStr = NULL;
365
366	if (domain) {
367		DomainName = CFStringCreateWithCString(kCFAllocatorDefault, domain, kCFStringEncodingUTF8);
368	}
369
370	if (user) {
371		Username = CFStringCreateWithCString(kCFAllocatorDefault, user, kCFStringEncodingUTF8);
372	}
373
374	if (passwd) {
375		Password = CFStringCreateWithCString(kCFAllocatorDefault, passwd, kCFStringEncodingUTF8);
376	}
377
378	if (server) {
379		ServerName = CFStringCreateWithCString(kCFAllocatorDefault, server, kCFStringEncodingUTF8);
380	}
381
382	if (path) {
383		FullPath = CFStringCreateWithCString(kCFAllocatorDefault, path, kCFStringEncodingUTF8);
384	}
385
386	if (port != -1) {
387		PortNumber = CFStringCreateWithFormat( kCFAllocatorDefault, NULL, CFSTR("%d"), port);
388	}
389	URLString = CreateURLCFString(DomainName, Username, Password, ServerName, FullPath, PortNumber);
390	if (URLString) {
391		CFIndex maxLen = (CFStringGetLength(URLString) * 3) + 1;
392		urlStr = calloc(1, maxLen);
393		if (urlStr) {
394			CFStringGetCString(URLString, urlStr, maxLen, kCFStringEncodingUTF8);
395		}
396		CFRelease(URLString);
397	}
398	if (DomainName) {
399		CFRelease(DomainName);
400	}
401	if (Username) {
402		CFRelease(Username);
403	}
404	if (Password) {
405		CFRelease(Password);
406	}
407	if (ServerName) {
408		CFRelease(ServerName);
409	}
410	if (FullPath) {
411		CFRelease(FullPath);
412	}
413	if (PortNumber) {
414		CFRelease(PortNumber);
415	}
416	return urlStr;
417}
418
419CFStringRef
420SMBCreateNetBIOSName(CFStringRef proposedName)
421{
422	CFMutableStringRef  composedName;
423	CFStringEncoding    codepage;
424	CFIndex		    nconverted = 0;
425	CFIndex		    nused = 0;
426
427	uint8_t		    name_buffer[NetBIOS_NAME_LEN];
428
429	if (proposedName == NULL) {
430	    return NULL;
431	}
432
433	codepage = getPrefsCodePage();
434
435	composedName = CFStringCreateMutableCopy(kCFAllocatorDefault,
436		0, proposedName);
437	if (!composedName) {
438	    return NULL;
439	}
440	CFStringTrimWhitespace(composedName);
441	CFStringUppercase(composedName, CFLocaleGetSystem());
442
443	nconverted = CFStringGetBytes(composedName,
444		CFRangeMake(0,
445		    MIN((CFIndex)sizeof(name_buffer), CFStringGetLength(composedName))),
446		codepage, 0 /* loss byte */, false /* no BOM */,
447		name_buffer, sizeof(name_buffer), &nused);
448
449	/* We expect the conversion above to always succeed, given that we
450	 * tried to remove anything that might not convert to a code page.
451	 */
452	if (nconverted == 0) {
453	    char buf[256];
454
455	    buf[0] = '\0';
456	    CFStringGetCString(composedName, buf, sizeof(buf), kCFStringEncodingUTF8);
457	    SMBLogInfo("failed to compose a NetBIOS name string from '%s'",
458		    ASL_LEVEL_DEBUG, buf);
459
460	    CFRelease(composedName);
461	    return NULL;
462	}
463
464	CFRelease(composedName);
465
466	/* Null terminate for the benefit of CFStringCreate. Be careful to be
467	 * no more that 15 bytes, since the last byte is reserved for the name
468	 * type.
469	 */
470	name_buffer[MIN(nused, (CFIndex)sizeof(name_buffer) - 1)] = '\0';
471
472	composedName = CFStringCreateMutable(kCFAllocatorDefault,
473		NetBIOS_NAME_LEN);
474	if (composedName == NULL) {
475	    return NULL;
476	}
477
478	CFStringAppendCString(composedName, (const char *)name_buffer, codepage);
479	return composedName;
480}
481
482
483void
484SMBRemountServer(const void	*inputBuffer, size_t inputBufferLen)
485{
486	fsid_t fsid = *(fsid_t *)inputBuffer;
487
488	if (inputBufferLen == sizeof(fsid)) {
489		int error = smb_remount_with_fsid(fsid);
490		if (error) {
491			SMBLogInfo("%s: error = %d inputBuffer = %p or inputBufferLen = %zu",
492				   ASL_LEVEL_DEBUG, __FUNCTION__, error, inputBuffer, inputBufferLen);
493		}
494	} else {
495		SMBLogInfo("%s: Bad inputBuffer = %p or inputBufferLen = %zu",
496			   ASL_LEVEL_DEBUG, __FUNCTION__, inputBuffer, inputBufferLen);
497	}
498}
499
500int
501SMBGetDfsReferral(const char * url, CFMutableDictionaryRef dfsReferralDict)
502{
503	int			error;
504	NTSTATUS	status;
505	SMBHANDLE	serverConnection;
506	void *      hContext = NULL;
507
508	if (!dfsReferralDict) {
509		errno = EINVAL;
510		return -1;
511	}
512
513	status = SMBOpenServerEx(url, &serverConnection, kSMBOptionSessionOnly);
514	/* SMBOpenServerEx sets errno, */
515	if (!NT_SUCCESS(status)) {
516		return -1;
517	}
518
519	status = SMBServerContext(serverConnection, &hContext);
520	if (!NT_SUCCESS(status)) {
521		/* Couldn't get the context? */
522		SMBReleaseServer(serverConnection);
523		return -1;
524	}
525
526	error = getDfsReferralList(hContext, dfsReferralDict);
527	if (error) {
528		SMBReleaseServer(serverConnection);
529		errno = error;
530		return -1;
531	}
532
533	SMBReleaseServer(serverConnection);
534	return 0;
535}
536