1 /*
2    Unix SMB/CIFS implementation.
3    server specific string routines
4    Copyright (C) Andrew Tridgell 2001
5    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003
6 
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11 
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16 
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20 
21 #include "includes.h"
22 #include "smbd/smbd.h"
23 #include "smbd/globals.h"
24 
25 /* Make sure we can't write a string past the end of the buffer */
26 
srvstr_push_fn(const char * base_ptr,uint16_t smb_flags2,void * dest,const char * src,int dest_len,int flags,size_t * ret_len)27 NTSTATUS srvstr_push_fn(const char *base_ptr, uint16_t smb_flags2, void *dest,
28 		      const char *src, int dest_len, int flags, size_t *ret_len)
29 {
30 	size_t len;
31 	int saved_errno;
32 	NTSTATUS status;
33 
34 	if (dest_len < 0) {
35 		return NT_STATUS_INVALID_PARAMETER;
36 	}
37 
38 	saved_errno = errno;
39 	errno = 0;
40 
41 	/* 'normal' push into size-specified buffer */
42 	len = push_string_base(base_ptr, smb_flags2, dest, src,
43 				dest_len, flags);
44 
45 	if (errno != 0) {
46 		/*
47 		 * Special case E2BIG, EILSEQ, EINVAL
48 		 * as they mean conversion errors here,
49 		 * but we don't generically map them as
50 		 * they can mean different things in
51 		 * generic filesystem calls (such as
52 		 * read xattrs).
53 		 */
54 		if (errno == E2BIG || errno == EILSEQ || errno == EINVAL) {
55 			status = NT_STATUS_ILLEGAL_CHARACTER;
56 		} else {
57 			status = map_nt_error_from_unix_common(errno);
58 			/*
59 			 * Paranoia - Filter out STATUS_MORE_ENTRIES.
60 			 * I don't think we can get this but it has a
61 			 * specific meaning to the client.
62 			 */
63 			if (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
64 				status = NT_STATUS_UNSUCCESSFUL;
65 			}
66 		}
67 		DEBUG(10,("character conversion failure "
68 			"on string (%s) (%s)\n",
69 			src, strerror(errno)));
70 	} else {
71 		/* Success - restore untouched errno. */
72 		errno = saved_errno;
73 		*ret_len = len;
74 		status = NT_STATUS_OK;
75 	}
76 	return status;
77 }
78 
79 /*******************************************************************
80  Add a string to the end of a smb_buf, adjusting bcc and smb_len.
81  Return the bytes added
82 ********************************************************************/
83 
message_push_string(uint8_t ** outbuf,const char * str,int flags)84 ssize_t message_push_string(uint8_t **outbuf, const char *str, int flags)
85 {
86 	size_t buf_size = smb_len(*outbuf) + 4;
87 	size_t grow_size;
88 	size_t result = 0;
89 	uint8_t *tmp;
90 	NTSTATUS status;
91 
92 	/*
93 	 * We need to over-allocate, now knowing what srvstr_push will
94 	 * actually use. This is very generous by incorporating potential
95 	 * padding, the terminating 0 and at most 4 chars per UTF-16 code
96 	 * point.
97 	 */
98 	grow_size = (strlen(str) + 2) * 4;
99 
100 	if (!(tmp = talloc_realloc(NULL, *outbuf, uint8_t,
101 					 buf_size + grow_size))) {
102 		DEBUG(0, ("talloc failed\n"));
103 		return -1;
104 	}
105 
106 	status = srvstr_push((char *)tmp, SVAL(tmp, smb_flg2),
107 			     tmp + buf_size, str, grow_size, flags, &result);
108 
109 	if (!NT_STATUS_IS_OK(status)) {
110 		DEBUG(0, ("srvstr_push failed\n"));
111 		return -1;
112 	}
113 
114 	/*
115 	 * Ensure we clear out the extra data we have
116 	 * grown the buffer by, but not written to.
117 	 */
118 	if (buf_size + result < buf_size) {
119 		return -1;
120 	}
121 	if (grow_size < result) {
122 		return -1;
123 	}
124 
125 	memset(tmp + buf_size + result, '\0', grow_size - result);
126 
127 	set_message_bcc((char *)tmp, smb_buflen(tmp) + result);
128 
129 	*outbuf = tmp;
130 
131 	return result;
132 }
133