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 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  *
26  */
27 
28 /* $Id: ipp-listener.c 146 2006-03-24 00:26:54Z njacobs $ */
29 
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <netinet/in.h>
34 #include <assert.h>
35 #include <errno.h>
36 #include <syslog.h>
37 #include <sys/types.h>
38 #include <sys/stat.h>
39 #include <fcntl.h>
40 #include <unistd.h>
41 #include <sys/systeminfo.h>
42 
43 #include <papi.h>
44 #include <ipp-listener.h>
45 #include <uri.h>
46 
47 typedef papi_status_t (ipp_handler_t)(papi_service_t svc,
48 					papi_attribute_t **request,
49 					papi_attribute_t ***response,
50 					ipp_reader_t iread, void *fd);
51 
52 /*
53  * protocol request handlers are inserted below.  The handler must be
54  * declared extern immediately below this comment and then an entry
55  * must be inserted in the "handlers" table a little further down.
56  */
57 extern ipp_handler_t ipp_print_job;
58 extern ipp_handler_t ipp_validate_job;
59 extern ipp_handler_t ipp_create_job;
60 extern ipp_handler_t ipp_get_printer_attributes;
61 extern ipp_handler_t ipp_get_jobs;
62 extern ipp_handler_t ipp_pause_printer;
63 extern ipp_handler_t ipp_resume_printer;
64 extern ipp_handler_t ipp_disable_printer;
65 extern ipp_handler_t ipp_enable_printer;
66 extern ipp_handler_t ipp_purge_jobs;
67 extern ipp_handler_t ipp_send_document;
68 extern ipp_handler_t ipp_cancel_job;
69 extern ipp_handler_t ipp_get_job_attributes;
70 extern ipp_handler_t ipp_release_job;
71 extern ipp_handler_t ipp_hold_job;
72 extern ipp_handler_t ipp_restart_job;
73 extern ipp_handler_t ipp_set_job_attributes;
74 extern ipp_handler_t ipp_set_printer_attributes;
75 extern ipp_handler_t cups_get_default;
76 extern ipp_handler_t cups_get_printers;
77 extern ipp_handler_t cups_get_classes;
78 extern ipp_handler_t cups_accept_jobs;
79 extern ipp_handler_t cups_reject_jobs;
80 extern ipp_handler_t cups_move_job;
81 
82 /* ARGSUSED0 */
83 static papi_status_t
default_handler(papi_service_t svc,papi_attribute_t ** request,papi_attribute_t *** response,ipp_reader_t iread,void * fd)84 default_handler(papi_service_t svc, papi_attribute_t **request,
85 		papi_attribute_t ***response, ipp_reader_t iread, void *fd)
86 {
87 	int result = (int)PAPI_INTERNAL_ERROR;
88 
89 	if (response != NULL)
90 		(void) papiAttributeListGetInteger(*response, NULL,
91 					"status-code", &result);
92 
93 	return ((papi_status_t)result);
94 }
95 
96 static struct {
97 	int16_t	id;
98 	char *name;
99 	ipp_handler_t *function;
100 	enum { OP_REQUIRED, OP_OPTIONAL, OP_VENDOR } type;
101 } handlers[] = {
102 	/* Printer Operations */
103 	{ 0x0002, "print-job",			ipp_print_job,	OP_REQUIRED },
104 	{ 0x0003, "print-uri",			NULL,		OP_OPTIONAL },
105 	{ 0x0004, "validate-job",		ipp_validate_job,
106 								OP_REQUIRED },
107 	{ 0x0005, "create-job",			ipp_create_job,	OP_OPTIONAL },
108 	{ 0x000a, "get-jobs",			ipp_get_jobs,	OP_REQUIRED },
109 	{ 0x000b, "get-printer-attributes",	ipp_get_printer_attributes,
110 								OP_REQUIRED },
111 	{ 0x0010, "pause-printer",		ipp_pause_printer,
112 								OP_OPTIONAL },
113 	{ 0x0011, "resume-printer",		ipp_resume_printer,
114 								OP_OPTIONAL },
115 	{ 0x0012, "purge-jobs",			ipp_purge_jobs,	OP_OPTIONAL },
116 	{ 0x0013, "set-printer-attributes",	ipp_set_printer_attributes,
117 								OP_OPTIONAL },
118 	{ 0x0014, "set-job-attributes",		ipp_set_job_attributes,
119 								OP_OPTIONAL },
120 	{ 0x0022, "enable-printer",		ipp_enable_printer,
121 								OP_OPTIONAL },
122 	{ 0x0023, "disable-printer",		ipp_disable_printer,
123 								OP_OPTIONAL },
124 	/* Job Operations */
125 	{ 0x0006, "send-document",		ipp_send_document,
126 								OP_OPTIONAL },
127 	{ 0x0007, "send-uri",			NULL,		OP_OPTIONAL },
128 	{ 0x0008, "cancel-job",			ipp_cancel_job,	OP_REQUIRED },
129 	{ 0x0009, "get-job-attributes",		ipp_get_job_attributes,
130 								OP_REQUIRED },
131 	{ 0x000c, "hold-job",			ipp_hold_job,	OP_OPTIONAL },
132 	{ 0x000d, "release-job",		ipp_release_job,
133 								OP_OPTIONAL },
134 	{ 0x000e, "restart-job",		ipp_restart_job,
135 								OP_OPTIONAL },
136 	/* Other Operations */
137 	{ 0x4001, "cups-get-default",		cups_get_default,
138 								OP_VENDOR },
139 	{ 0x4002, "cups-get-printers",		cups_get_printers,
140 								OP_VENDOR },
141 	{ 0x4005, "cups-get-classes",		cups_get_classes,
142 								OP_VENDOR },
143 	{ 0x4008, "cups-accept-jobs",		cups_accept_jobs,
144 								OP_VENDOR },
145 	{ 0x4009, "cups-reject-jobs",		cups_reject_jobs,
146 								OP_VENDOR },
147 	{ 0x400D, "cups-move-job",		cups_move_job,	OP_VENDOR },
148 	{ 0, NULL, NULL, OP_VENDOR }
149 };
150 
151 static int
ipp_operation_name_to_index(char * name)152 ipp_operation_name_to_index(char *name)
153 {
154 	int i;
155 
156 	for (i = 0; handlers[i].name != NULL; i++)
157 		if (strcasecmp(name, handlers[i].name) == 0)
158 			return (i);
159 
160 	return (-1);
161 }
162 
163 static int
ipp_operation_id_to_index(int16_t id)164 ipp_operation_id_to_index(int16_t id)
165 {
166 	int i;
167 
168 	for (i = 0; handlers[i].name != NULL; i++)
169 		if (id == handlers[i].id)
170 			return (i);
171 
172 	return (-1);
173 }
174 
175 static ipp_handler_t *
ipp_operation_handler(papi_attribute_t ** request,papi_attribute_t *** response)176 ipp_operation_handler(papi_attribute_t **request, papi_attribute_t ***response)
177 {
178 	int id = 0;
179 	int index;
180 	papi_attribute_t **ops = NULL;
181 	papi_status_t status;
182 	char configured = PAPI_FALSE;
183 
184 	/* get the operation from the request */
185 	status = papiAttributeListGetInteger(request, NULL,
186 				"operation-id", &id);
187 	if (status != PAPI_OK) {
188 		ipp_set_status(response, PAPI_BAD_ARGUMENT,
189 			"no operation specified in request");
190 		return (default_handler);
191 	}
192 
193 	/* find the operation in the handler table */
194 	index = ipp_operation_id_to_index(id);
195 #ifdef DEBUG
196 	if (index == -1)
197 		fprintf(stderr, "Operation: 0x%4.4x\n", id);
198 	else
199 		fprintf(stderr, "Operation: 0x%4.4x(%s)\n", id,
200 			handlers[index].name);
201 	fflush(stderr);
202 #endif
203 
204 	if ((index == -1) || (handlers[index].function == NULL)) {
205 		ipp_set_status(response, PAPI_OPERATION_NOT_SUPPORTED,
206 			"operation (0x%4.4x) not implemented by server",
207 			id);
208 		return (default_handler);
209 	}
210 
211 	/* find the configured operations */
212 	status = papiAttributeListGetCollection(request, NULL,
213 				"operations", &ops);
214 	if (status != PAPI_OK) {	/* this should not be possible */
215 		ipp_set_status(response, PAPI_INTERNAL_ERROR,
216 			"sofware error, no operations configured");
217 		return (default_handler);
218 	}
219 
220 	/* check if the requested operation is configured */
221 	status = papiAttributeListGetBoolean(ops, NULL,
222 				handlers[index].name, &configured);
223 	if ((status != PAPI_OK) || (configured != PAPI_TRUE)) {
224 		ipp_set_status(response, PAPI_OPERATION_NOT_SUPPORTED,
225 			"operation (%s 0x%4.4x) not enabled on server",
226 			handlers[index].name, id);
227 		return (default_handler);
228 	}
229 
230 	return (handlers[index].function);
231 }
232 
233 static char
type_to_boolean(char * type)234 type_to_boolean(char *type)
235 {
236 	char result = PAPI_FALSE;
237 
238 	if ((strcasecmp(type, "true") == 0) ||
239 	    (strcasecmp(type, "yes") == 0) ||
240 	    (strcasecmp(type, "on") == 0) ||
241 	    (strcasecmp(type, "enable") == 0))
242 		result = PAPI_TRUE;
243 
244 	return (result);
245 }
246 
247 static papi_status_t
ipp_configure_required_operations(papi_attribute_t *** list,char boolean)248 ipp_configure_required_operations(papi_attribute_t ***list, char boolean)
249 {
250 	papi_status_t result = PAPI_OK;
251 	int i;
252 
253 	for (i = 0; ((result == PAPI_OK) && (handlers[i].name != NULL)); i++)
254 		if (handlers[i].type == OP_REQUIRED)
255 			result = papiAttributeListAddBoolean(list,
256 					PAPI_ATTR_REPLACE, handlers[i].name,
257 					boolean);
258 
259 	return (result);
260 
261 }
262 
263 static papi_status_t
ipp_configure_all_operations(papi_attribute_t *** list,char boolean)264 ipp_configure_all_operations(papi_attribute_t ***list, char boolean)
265 {
266 	papi_status_t result = PAPI_OK;
267 	int i;
268 
269 	for (i = 0; ((result == PAPI_OK) && (handlers[i].name != NULL)); i++)
270 		result = papiAttributeListAddBoolean(list, PAPI_ATTR_REPLACE,
271 				handlers[i].name, boolean);
272 
273 	return (result);
274 }
275 
276 papi_status_t
ipp_configure_operation(papi_attribute_t *** list,char * operation,char * type)277 ipp_configure_operation(papi_attribute_t ***list, char *operation, char *type)
278 {
279 	papi_status_t result = PAPI_OPERATION_NOT_SUPPORTED;
280 	char boolean = PAPI_FALSE;
281 
282 	if ((list == NULL) || (operation == NULL) || (type == NULL))
283 		return (PAPI_BAD_ARGUMENT);
284 
285 	boolean = type_to_boolean(type);
286 
287 	if (strcasecmp(operation, "all") == 0) {
288 		result = ipp_configure_all_operations(list, boolean);
289 	} else if (strcasecmp(operation, "required") == 0) {
290 		result = ipp_configure_required_operations(list, boolean);
291 	} else if (ipp_operation_name_to_index(operation) != -1) {
292 		result = papiAttributeListAddBoolean(list, PAPI_ATTR_REPLACE,
293 							operation, boolean);
294 	}
295 
296 	return (result);
297 }
298 
299 void
ipp_operations_supported(papi_attribute_t *** list,papi_attribute_t ** request)300 ipp_operations_supported(papi_attribute_t ***list, papi_attribute_t **request)
301 {
302 	papi_attribute_t **group = NULL;
303 
304 	(void) papiAttributeListGetCollection(request, NULL,
305 				"operations", &group);
306 	if (group != NULL) {
307 		int i;
308 
309 		for (i = 0; handlers[i].name != NULL; i++) {
310 			char boolean = PAPI_FALSE;
311 			(void) papiAttributeListGetBoolean(group, NULL,
312 					handlers[i].name, &boolean);
313 
314 			if (boolean == PAPI_TRUE)
315 				(void) papiAttributeListAddInteger(list,
316 					PAPI_ATTR_APPEND,
317 					"operations-supported",
318 					handlers[i].id);
319 		}
320 	}
321 }
322 
323 static papi_status_t
ipp_initialize_response(papi_attribute_t ** request,papi_attribute_t *** response)324 ipp_initialize_response(papi_attribute_t **request,
325 			papi_attribute_t ***response)
326 {
327 	papi_attribute_t **operational = NULL;
328 	int i;
329 
330 	if ((request == NULL) || (response == NULL))
331 		return (PAPI_BAD_ARGUMENT);
332 
333 	/* If the response was initialized, start over */
334 	if (*response != NULL) {
335 		papiAttributeListFree(*response);
336 		*response = NULL;
337 	}
338 
339 	/* Add the basic ipp header information to the response */
340 	(void) papiAttributeListGetInteger(request, NULL, "version-major", &i);
341 	(void) papiAttributeListAddInteger(response, PAPI_ATTR_REPLACE,
342 					"version-major", i);
343 	(void) papiAttributeListGetInteger(request, NULL, "version-minor", &i);
344 	(void) papiAttributeListAddInteger(response, PAPI_ATTR_REPLACE,
345 					"version-minor", i);
346 
347 	(void) papiAttributeListGetInteger(request, NULL, "request-id", &i);
348 	(void) papiAttributeListAddInteger(response, PAPI_ATTR_REPLACE,
349 					"request-id", i);
350 
351 	/* Add a default operational attributes group to the response */
352 	(void) papiAttributeListAddString(&operational, PAPI_ATTR_EXCL,
353 			"attributes-charset", "utf-8");
354 	(void) papiAttributeListAddString(&operational, PAPI_ATTR_EXCL,
355 			"attributes-natural-language", "en-us");
356 
357 	(void) papiAttributeListAddCollection(response, PAPI_ATTR_REPLACE,
358 				"operational-attributes-group", operational);
359 	papiAttributeListFree(operational);
360 
361 	return (PAPI_OK);
362 }
363 
364 /* simplistic check for cyclical service references */
365 static int
cyclical_service_check(char * svc_name,int port)366 cyclical_service_check(char *svc_name, int port)
367 {
368 	papi_attribute_t **list;
369 	char buf[BUFSIZ];
370 	uri_t *uri = NULL;
371 	char *s = NULL;
372 
373 	/* was there a service_uri? */
374 	if (svc_name == NULL)
375 		return (0);
376 
377 	if ((list = getprinterbyname(svc_name, NULL)) == NULL)
378 		return (0);	/* if it doesnt' resolve, we will fail later */
379 
380 	papiAttributeListGetString(list, NULL, "printer-uri-supported", &s);
381 	if ((s == NULL) || (strcasecmp(svc_name, s) != 0))
382 		return (0); 	/* they don't match */
383 
384 	/* is it in uri form? */
385 	if (uri_from_string(s, &uri) < 0)
386 		return (0);
387 
388 	if ((uri == NULL) || (uri->scheme == NULL) || (uri->host == NULL)) {
389 		uri_free(uri);
390 		return (0);
391 	}
392 
393 	/* is it ipp form */
394 	if (strcasecmp(uri->scheme, "ipp") != 0) {
395 		uri_free(uri);
396 		return (0);
397 	}
398 
399 	/* does the host match up */
400 	if (is_localhost(uri->host) != 0) {
401 		uri_free(uri);
402 		return (0);
403 	}
404 
405 	/* does the port match our own */
406 	if (((uri->port == NULL) && (port != 631)) ||
407 	    ((uri->port != NULL) && (atoi(uri->port) != port))) {
408 		uri_free(uri);
409 		return (0);
410 	}
411 
412 	uri_free(uri);
413 
414 	return (1);
415 }
416 
417 static papi_status_t
print_service_connect(papi_service_t * svc,papi_attribute_t ** request,papi_attribute_t *** response)418 print_service_connect(papi_service_t *svc, papi_attribute_t **request,
419 		papi_attribute_t ***response)
420 {
421 	papi_status_t status;
422 	papi_attribute_t **operational = NULL;
423 	char *printer_uri = NULL;
424 	char *svc_name = NULL;
425 	char *user = NULL;
426 	int port = 631;
427 
428 	/* Get the operational attributes group from the request */
429 	(void) papiAttributeListGetCollection(request, NULL,
430 				"operational-attributes-group", &operational);
431 
432 	/* get the user name */
433 	(void) papiAttributeListGetString(request, NULL, "default-user", &user);
434 	(void) papiAttributeListGetString(operational, NULL,
435 				"requesting-user-name", &user);
436 
437 	/* get the printer or service name */
438 	(void) papiAttributeListGetString(request, NULL,
439 				"default-service", &svc_name);
440 	get_printer_id(operational, &svc_name, NULL);
441 
442 	/* get the port that we are listening on */
443 	(void) papiAttributeListGetInteger(request, NULL, "uri-port", &port);
444 
445 	if (cyclical_service_check(svc_name, port) != 0) {
446 		status = PAPI_NOT_POSSIBLE;
447 		ipp_set_status(response, status, "printer-uri is cyclical");
448 		return (status);
449 	}
450 
451 	status = papiServiceCreate(svc, svc_name, user, NULL, NULL,
452 					PAPI_ENCRYPT_NEVER, NULL);
453 	if (status != PAPI_OK) {
454 		ipp_set_status(response, status, "print service: %s",
455 				papiStatusString(status));
456 		return (status);
457 	}
458 
459 	/*
460 	 * Trusted Solaris can't be trusting of intermediaries.  Pass
461 	 * the socket connection to the print service to retrieve the
462 	 * sensativity label off of a multi-level port.
463 	 */
464 	{
465 		int fd = -1;
466 
467 		(void) papiAttributeListGetInteger(request, NULL,
468 					"peer-socket", &fd);
469 		if (fd != -1)
470 			papiServiceSetPeer(*svc, fd);
471 	}
472 
473 	return (status);
474 }
475 
476 papi_status_t
ipp_process_request(papi_attribute_t ** request,papi_attribute_t *** response,ipp_reader_t iread,void * fd)477 ipp_process_request(papi_attribute_t **request, papi_attribute_t ***response,
478 	ipp_reader_t iread, void *fd)
479 {
480 	papi_status_t result = PAPI_OK;
481 
482 	ipp_initialize_response(request, response);
483 
484 #ifdef DEBUG
485 	fprintf(stderr, "REQUEST:");
486 	papiAttributeListPrint(stderr, request, " %d  ", getpid());
487 	fprintf(stderr, "\n");
488 #endif
489 
490 	/* verify that the request is "well-formed" */
491 	if ((result = ipp_validate_request(request, response)) == PAPI_OK) {
492 		papi_service_t svc = NULL;
493 		ipp_handler_t *handler;
494 
495 		result = print_service_connect(&svc, request, response);
496 		handler = ipp_operation_handler(request, response);
497 
498 		/* process the request */
499 		if ((result == PAPI_OK) && (handler != NULL))
500 			result = (handler)(svc, request, response, iread, fd);
501 #ifdef DEBUG
502 		fprintf(stderr, "RESULT: %s\n", papiStatusString(result));
503 #endif
504 		papiServiceDestroy(svc);
505 	}
506 
507 	(void) papiAttributeListAddInteger(response, PAPI_ATTR_EXCL,
508 				"status-code", result);
509 	massage_response(request, *response);
510 
511 #ifdef DEBUG
512 	fprintf(stderr, "RESPONSE:");
513 	papiAttributeListPrint(stderr, *response, " %d  ", getpid());
514 	fprintf(stderr, "\n");
515 #endif
516 
517 	return (result);
518 }
519