1 /* -*- c-basic-offset: 8 -*-
2    rdesktop: A Remote Desktop Protocol client.
3    Entrypoint and utility functions
4    Copyright (C) Matthew Chapman <matthewc.unsw.edu.au> 1999-2008
5    Copyright (C) Jeroen Meijer <jeroen@oldambt7.com> 2003-2008
6    Copyright (C) Henrik Andersson <hean01@cendio.se> 2013-2017
7 
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 3 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, see <http://www.gnu.org/licenses/>.
21 */
22 
23 /* According to the W2K RDP Printer Redirection WhitePaper, a data
24  * blob is sent to the client after the configuration of the printer
25  * is changed at the server.
26  *
27  * This data blob is saved to the registry. The client returns this
28  * data blob in a new session with the printer announce data.
29  * The data is not interpreted by the client.
30  */
31 
32 #include <sys/stat.h>
33 #include <sys/types.h>
34 #include <errno.h>
35 #include <fcntl.h>
36 #include <unistd.h>
37 #include <string.h>
38 #include "rdesktop.h"
39 
40 static RD_BOOL
printercache_mkdir(char * base,char * printer)41 printercache_mkdir(char *base, char *printer)
42 {
43 	char *path;
44 
45 	path = (char *) xmalloc(strlen(base) + sizeof("/.rdesktop/rdpdr/") + strlen(printer) + 1);
46 
47 	sprintf(path, "%s/.rdesktop", base);
48 	if ((mkdir(path, 0700) == -1) && errno != EEXIST)
49 	{
50 		logger(Core, Error, "printercache_mkdir(), mkdir() failed: %s", strerror(errno));
51 		xfree(path);
52 		return False;
53 	}
54 
55 	strcat(path, "/rdpdr");
56 	if ((mkdir(path, 0700) == -1) && errno != EEXIST)
57 	{
58 		logger(Core, Error, "printercache_mkdir(), mkdir() failed: %s", strerror(errno));
59 		xfree(path);
60 		return False;
61 	}
62 
63 	strcat(path, "/");
64 	strcat(path, printer);
65 	if ((mkdir(path, 0700) == -1) && errno != EEXIST)
66 	{
67 		logger(Core, Error, "printercache_mkdir(), mkdir() failed: %s", strerror(errno));
68 		xfree(path);
69 		return False;
70 	}
71 
72 	xfree(path);
73 	return True;
74 }
75 
76 static RD_BOOL
printercache_unlink_blob(char * printer)77 printercache_unlink_blob(char *printer)
78 {
79 	char *path;
80 	char *home;
81 
82 	if (printer == NULL)
83 		return False;
84 
85 	home = getenv("HOME");
86 	if (home == NULL)
87 		return False;
88 
89 	path = (char *) xmalloc(strlen(home) + sizeof("/.rdesktop/rdpdr/") + strlen(printer) +
90 				sizeof("/AutoPrinterCacheData") + 1);
91 
92 	sprintf(path, "%s/.rdesktop/rdpdr/%s/AutoPrinterCacheData", home, printer);
93 
94 	if (unlink(path) < 0)
95 	{
96 		xfree(path);
97 		return False;
98 	}
99 
100 	sprintf(path, "%s/.rdesktop/rdpdr/%s", home, printer);
101 
102 	if (rmdir(path) < 0)
103 	{
104 		xfree(path);
105 		return False;
106 	}
107 
108 	xfree(path);
109 	return True;
110 }
111 
112 
113 static RD_BOOL
printercache_rename_blob(char * printer,char * new_printer)114 printercache_rename_blob(char *printer, char *new_printer)
115 {
116 	char *printer_path;
117 	char *new_printer_path;
118 	int printer_maxlen;
119 
120 	char *home;
121 
122 	if (printer == NULL)
123 		return False;
124 
125 	home = getenv("HOME");
126 	if (home == NULL)
127 		return False;
128 
129 	printer_maxlen =
130 		(strlen(printer) >
131 		 strlen(new_printer) ? strlen(printer) : strlen(new_printer)) + strlen(home) +
132 		sizeof("/.rdesktop/rdpdr/") + 1;
133 
134 	printer_path = (char *) xmalloc(printer_maxlen);
135 	new_printer_path = (char *) xmalloc(printer_maxlen);
136 
137 	sprintf(printer_path, "%s/.rdesktop/rdpdr/%s", home, printer);
138 	sprintf(new_printer_path, "%s/.rdesktop/rdpdr/%s", home, new_printer);
139 
140 	logger(Core, Debug, "printercache_rename_blob(), printer_path=%s, new_printer_path=%s",
141 	       printer_path, new_printer_path);
142 	if (rename(printer_path, new_printer_path) < 0)
143 	{
144 		logger(Core, Error, "printercache_rename_blob(), rename() failed: %s",
145 		       strerror(errno));
146 		xfree(printer_path);
147 		xfree(new_printer_path);
148 		return False;
149 	}
150 
151 	xfree(printer_path);
152 	xfree(new_printer_path);
153 	return True;
154 }
155 
156 
157 int
printercache_load_blob(char * printer_name,uint8 ** data)158 printercache_load_blob(char *printer_name, uint8 ** data)
159 {
160 	char *home, *path;
161 	struct stat st;
162 	int fd, length;
163 
164 	if (printer_name == NULL)
165 		return 0;
166 
167 	*data = NULL;
168 
169 	home = getenv("HOME");
170 	if (home == NULL)
171 		return 0;
172 
173 	path = (char *) xmalloc(strlen(home) + sizeof("/.rdesktop/rdpdr/") + strlen(printer_name) +
174 				sizeof("/AutoPrinterCacheData") + 1);
175 	sprintf(path, "%s/.rdesktop/rdpdr/%s/AutoPrinterCacheData", home, printer_name);
176 
177 	fd = open(path, O_RDONLY);
178 	if (fd == -1)
179 	{
180 		xfree(path);
181 		return 0;
182 	}
183 
184 	if (fstat(fd, &st))
185 	{
186 		xfree(path);
187 		return 0;
188 	}
189 
190 	*data = (uint8 *) xmalloc(st.st_size);
191 	length = read(fd, *data, st.st_size);
192 	close(fd);
193 	xfree(path);
194 	return length;
195 }
196 
197 static void
printercache_save_blob(char * printer_name,uint8 * data,uint32 length)198 printercache_save_blob(char *printer_name, uint8 * data, uint32 length)
199 {
200 	char *home, *path;
201 	int fd;
202 
203 	if (printer_name == NULL)
204 		return;
205 
206 	home = getenv("HOME");
207 	if (home == NULL)
208 		return;
209 
210 	if (!printercache_mkdir(home, printer_name))
211 		return;
212 
213 	path = (char *) xmalloc(strlen(home) + sizeof("/.rdesktop/rdpdr/") + strlen(printer_name) +
214 				sizeof("/AutoPrinterCacheData") + 1);
215 	sprintf(path, "%s/.rdesktop/rdpdr/%s/AutoPrinterCacheData", home, printer_name);
216 
217 	fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
218 	if (fd == -1)
219 	{
220 		logger(Core, Error, "printercache_save_blob(), open() failed: %s", strerror(errno));
221 		xfree(path);
222 		return;
223 	}
224 
225 	if (write(fd, data, length) != length)
226 	{
227 		logger(Core, Error, "printercache_save_blob(), write() failed: %s",
228 		       strerror(errno));
229 		unlink(path);
230 	}
231 
232 	close(fd);
233 	xfree(path);
234 }
235 
236 void
printercache_process(STREAM s)237 printercache_process(STREAM s)
238 {
239 	uint32 type, printer_length, driver_length, printer_unicode_length, blob_length;
240 	char device_name[9], *printer, *driver;
241 	size_t blob_start;
242 	unsigned char *blob;
243 
244 	printer = driver = NULL;
245 
246 	in_uint32_le(s, type);
247 	switch (type)
248 	{
249 		case 4:	/* rename item */
250 			in_uint8(s, printer_length);
251 			in_uint8s(s, 0x3);	/* padding */
252 			in_uint8(s, driver_length);
253 			in_uint8s(s, 0x3);	/* padding */
254 
255 			/* NOTE - 'driver' doesn't contain driver, it contains the new printer name */
256 
257 			rdp_in_unistr(s, printer_length, &printer, &printer_length);
258 			rdp_in_unistr(s, driver_length, &driver, &driver_length);
259 
260 			if (printer != NULL && driver != NULL)
261 				printercache_rename_blob(printer, driver);
262 
263 			free(printer);
264 			free(driver);
265 			break;
266 
267 		case 3:	/* delete item */
268 			in_uint8(s, printer_unicode_length);
269 			in_uint8s(s, 0x3);	/* padding */
270 			rdp_in_unistr(s, printer_unicode_length, &printer, &printer_unicode_length);
271 			if (printer)
272 				printercache_unlink_blob(printer);
273 			free(printer);
274 			break;
275 
276 		case 2:	/* save printer data */
277 			in_uint32_le(s, printer_unicode_length);
278 			in_uint32_le(s, blob_length);
279 
280 			if (printer_unicode_length < 2 * 255)
281 			{
282 				rdp_in_unistr(s, printer_unicode_length, &printer,
283 					      &printer_unicode_length);
284 				if (printer) {
285 					in_uint8p(s, blob, blob_length);
286 					printercache_save_blob(printer, blob, blob_length);
287 				}
288 				free(printer);
289 			}
290 			break;
291 
292 		case 1:	/* save device data */
293 			in_uint8a(s, device_name, 5);	/* get LPTx/COMx name */
294 
295 			/* need to fetch this data so that we can get the length of the packet to store. */
296 			blob_start = s_tell(s);
297 			in_uint8s(s, 0x2);	/* ??? */
298 			in_uint8s(s, 0x2)	/* pad?? */
299 				in_uint32_be(s, driver_length);
300 			in_uint32_be(s, printer_length);
301 			in_uint8s(s, 0x7)	/* pad?? */
302 				/* next is driver in unicode */
303 				/* next is printer in unicode */
304 				/* TODO: figure out how to use this information when reconnecting */
305 				/* actually - all we need to store is the driver and printer */
306 				/* and figure out what the first word is. */
307 				/* rewind stream so that we can save this blob   */
308 				/* length is driver_length + printer_length + 19 */
309 				/* rewind stream */
310 			s_seek(s, blob_start);
311 
312 			blob_length = driver_length + printer_length + 19;
313 			in_uint8p(s, blob, blob_length);
314 			printercache_save_blob(device_name, blob, blob_length);
315 			break;
316 		default:
317 			logger(Protocol, Warning,
318 			       "printercache_process(), unhandled packet type %d", type);
319 			break;
320 	}
321 }
322