1/*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License as
4 * published by the Free Software Foundation; either version 2 of
5 * the License, or (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
15 * MA 02111-1307 USA
16 */
17/***************************************************************************
18 * LPRng - An Extended Print Spooler System
19 *
20 * Copyright 1988-2003, Patrick Powell, San Diego, CA
21 *     papowell@lprng.com
22 * See LICENSE for conditions of use.
23 *
24 ***************************************************************************/
25
26 static char *const _id =
27"$Id: lpd_secure.c,v 1.1.1.1 2008/10/15 03:28:27 james26_jang Exp $";
28
29
30#include "lp.h"
31#include "user_auth.h"
32#include "lpd_dispatch.h"
33#include "getopt.h"
34#include "getqueue.h"
35#include "proctitle.h"
36#include "permission.h"
37#include "linksupport.h"
38#include "errorcodes.h"
39#include "fileopen.h"
40#include "lpd_rcvjob.h"
41#include "child.h"
42#include "globmatch.h"
43#include "lpd_jobs.h"
44#include "krb5_auth.h"
45#include "lpd_secure.h"
46
47/**** ENDINCLUDE ****/
48
49/***************************************************************************
50 * Commentary:
51 * Patrick Powell Mon Apr 17 05:43:48 PDT 1995
52 *
53 * The protocol used to send a secure job consists of the following
54 * following:
55 *
56 * \REQ_SECUREprintername C/F user authtype\n         - receive a command
57 *              0           1   2  3        4
58 * \REQ_SECUREprintername C/F user authtype jobsize\n - receive a job
59 *              0           1   2  3        4
60 *
61 * The server will return an ACK, and then start the authentication
62 * process.  See README.security for details.
63 *
64 ***************************************************************************/
65
66/*************************************************************************
67 * Receive_secure() - receive a secure transfer
68 *************************************************************************/
69#ifdef ORIGINAL_DEBUG//JY@1020
70int Receive_secure( int *sock, char *input )
71{
72	char *printername;
73	char error[SMALLBUFFER];	/* error message */
74	char *authtype;
75	char *cf, *s;
76	char *jobsize = 0;
77	char *user = 0;
78	int tempfd = -1;
79	int ack, status, from_server;
80	struct line_list args, header_info, info;
81	struct stat statb;
82	char *tempfile = 0;
83	struct security *security = 0;
84
85	Name = "RCVSEC";
86	memset( error, 0, sizeof(error));
87	ack = 0;
88	status = 0;
89
90	DEBUGF(DRECV1)("Receive_secure: input line '%s'", input );
91	Init_line_list( &args );
92	Init_line_list( &header_info );
93	Init_line_list( &info );
94
95	Split(&args,input+1,Whitespace,0,0,0,0,0,0);
96	DEBUGFC(DRECV1)Dump_line_list("Receive_secure - input", &args);
97	if( args.count != 5 && args.count != 4 ){
98		SNPRINTF( error+1, sizeof(error)-1)
99			_("bad command line '%s'"), input );
100		ack = ACK_FAIL;	/* no retry, don't send again */
101		status = JFAIL;
102		goto error;
103	}
104	Check_max(&args,1);
105	args.list[args.count] = 0;
106
107	/*
108     * \REQ_SECUREprintername C/F user authtype jobsize\n - receive a job
109     *              0           1   2  3        4
110	 */
111	printername = args.list[0];
112	cf = args.list[1];
113	user = args.list[2];	/* user is escape encoded */
114	Unescape(user);
115	authtype = args.list[3];
116	Unescape(authtype);
117	jobsize = args.list[4];
118
119	setproctitle( "lpd %s '%s'", Name, printername );
120
121	Perm_check.authtype = authtype;
122	from_server = 0;
123	if( *cf == 'F' ){
124		from_server = 1;
125	}
126
127	/* set up the authentication support information */
128
129	if( Is_clean_name( printername ) ){
130		SNPRINTF( error+1, sizeof(error)-1)
131			_("bad printer name '%s'"), input );
132		ack = ACK_FAIL;	/* no retry, don't send again */
133		status = JFAIL;
134		goto error;
135	}
136
137	Set_DYN(&Printer_DYN,printername);
138
139	if( Setup_printer( printername, error+1, sizeof(error)-1, 0 ) ){
140		if( jobsize ){
141			SNPRINTF( error+1, sizeof(error)-1)
142				_("bad printer '%s'"), printername );
143			ack = ACK_FAIL;	/* no retry, don't send again */
144			status = JFAIL;
145			goto error;
146		}
147	} else {
148		int db, dbf;
149
150		db = Debug;
151		dbf = DbgFlag;
152		s = Find_str_value(&Spool_control,DEBUG,Value_sep);
153		if(!s) s = New_debug_DYN;
154		Parse_debug( s, 0 );
155
156		if( !(DRECVMASK & DbgFlag) ){
157			Debug = db;
158			DbgFlag = dbf;
159		} else {
160			int tdb, tdbf;
161			tdb = Debug;
162			tdbf = DbgFlag;
163			Debug = db;
164			DbgFlag = dbf;
165			if( Log_file_DYN ){
166				tempfd = Checkwrite( Log_file_DYN, &statb,0,0,0);
167				if( tempfd > 0 && tempfd != 2 ){
168					dup2(tempfd,2);
169					close(tempfd);
170				}
171				tempfd = -1;
172			}
173			Debug = tdb;
174			DbgFlag = tdbf;
175			LOGDEBUG("Receive_secure: socket fd %d", *sock);
176			Dump_line_list("Receive_secure - input", &args);
177		}
178		DEBUGF(DRECV1)("Receive_secure: debug '%s', Debug %d, DbgFlag 0x%x",
179			s, Debug, DbgFlag );
180	}
181
182	if( !(security = Fix_receive_auth(authtype, &info)) ){
183		SNPRINTF( error+1, sizeof(error)-1)
184			_("unsupported authentication '%s'"), authtype );
185		ack = ACK_FAIL;	/* no retry, don't send again */
186		status = JFAIL;
187		goto error;
188	}
189	if( !security->server_receive ){
190		SNPRINTF( error+1, sizeof(error)-1)
191			_("no receive method supported for '%s'"), authtype );
192		ack = ACK_FAIL;	/* no retry, don't send again */
193		status = JFAIL;
194		goto error;
195	}
196
197
198	if( jobsize ){
199		double read_len;
200		read_len = strtod(jobsize,0);
201
202		DEBUGF(DRECV2)("Receive_secure: spooling_disabled %d",
203			Sp_disabled(&Spool_control) );
204		if( Sp_disabled(&Spool_control) ){
205			SNPRINTF( error+1, sizeof(error)-1)
206				_("%s: spooling disabled"), Printer_DYN );
207			ack = ACK_RETRY;	/* retry */
208			status = JFAIL;
209			goto error;
210		}
211		if( Max_job_size_DYN > 0 && (read_len+1023)/1024 > Max_job_size_DYN ){
212			SNPRINTF( error+1, sizeof(error)-1)
213				_("%s: job size %0.0f is larger than %d K"),
214				Printer_DYN, read_len, Max_job_size_DYN );
215			ack = ACK_RETRY;
216			status = JFAIL;
217			goto error;
218		} else if( !Check_space( read_len, Minfree_DYN, Spool_dir_DYN ) ){
219			SNPRINTF( error+1, sizeof(error)-1)
220				_("%s: insufficient file space"), Printer_DYN );
221			ack = ACK_RETRY;
222			status = JFAIL;
223			goto error;
224		}
225	}
226
227	tempfd = Make_temp_fd(&tempfile);
228	close(tempfd); tempfd = -1;
229
230	DEBUGF(DRECV1)("Receive_secure: sock %d, user '%s', jobsize '%s'",
231		*sock, user, jobsize );
232
233	status = security->server_receive( sock,
234		user, jobsize, from_server, authtype,
235		&info,
236		error+1, sizeof(error)-1,
237		&header_info,
238		security, tempfile );
239
240 error:
241	DEBUGF(DRECV1)("Receive_secure: status %d, ack %d, error '%s'",
242		status, ack, error+1 );
243
244	if( status ){
245		if( ack == 0 ) ack = ACK_FAIL;
246		error[0] = ack;
247		DEBUGF(DRECV1)("Receive_secure: sending '%s'", error );
248		(void)Link_send( ShortRemote_FQDN, sock,
249			Send_query_rw_timeout_DYN, error, safestrlen(error), 0 );
250		Errorcode = JFAIL;
251	}
252
253	Free_line_list( &args );
254	Free_line_list( &header_info );
255	Free_line_list( &info );
256
257	close( *sock ); *sock = -1;
258	Remove_tempfiles();
259
260	if( status == 0 && jobsize ){
261		/* start a new server */
262		DEBUGF(DRECV1)("Receive_secure: starting server");
263		if( Server_queue_name_DYN ){
264			Do_queue_jobs( Server_queue_name_DYN, 0 );
265		} else {
266			Do_queue_jobs( Printer_DYN, 0 );
267		}
268	}
269	cleanup(0);
270	return(0);
271}
272#endif
273
274#ifdef ORIGINAL_DEBUG//JY@1020
275int Do_secure_work( char *jobsize, int from_server,
276	char *tempfile, struct line_list *header_info )
277{
278	int n, len, linecount = 0, done = 0, fd, status = 0;
279	char *s, *t;
280	char buffer[SMALLBUFFER];
281	char error[SMALLBUFFER];
282	struct stat statb;
283
284	error[0] = 0;
285	if( (fd = Checkread(tempfile,&statb)) < 0 ){
286		status = JFAIL;
287		SNPRINTF( error, sizeof(error))
288			"Do_secure_work: reopen of '%s' failed - %s",
289				tempfile, Errormsg(errno));
290		goto error;
291	}
292
293	buffer[0] = 0;
294	n = 0;
295	done = 0;
296	linecount = 0;
297
298	while( !done && n < (int)sizeof(buffer)-1
299		&& (len = read( fd, buffer+n, sizeof(buffer)-1-n )) > 0 ){
300		buffer[n+len] = 0;
301		DEBUGF(DRECV1)("Do_secure_work: read %d - '%s'", len, buffer );
302		while( !done && (s = safestrchr(buffer,'\n')) ){
303			*s++ = 0;
304			if( safestrlen(buffer) == 0 ){
305				done = 1;
306				break;
307			}
308			DEBUGF(DRECV1)("Do_secure_work: line [%d] '%s'", linecount, buffer );
309			if( (t = strchr(buffer,'=')) ){
310				*t++ = 0;
311				Unescape(t);
312				Set_str_value(header_info, buffer, t );
313			} else {
314				switch( linecount ){
315					case 0:
316						if( jobsize ){
317							if( from_server ){
318								Set_str_value(header_info,CLIENT,buffer);
319							}
320							done = 1;
321						} else {
322							Set_str_value(header_info,INPUT,buffer); break;
323						}
324						break;
325					case 1:
326						Set_str_value(header_info,CLIENT,buffer);
327						done = 1;
328						break;
329				}
330			}
331			++linecount;
332			memmove(buffer,s,safestrlen(s)+1);
333			n = safestrlen(buffer);
334		}
335	}
336
337	if( fd >= 0 ) close(fd); fd = -1;
338
339	DEBUGFC(DRECV1)Dump_line_list("Do_secure_work - header", header_info );
340
341	if( (status = Check_secure_perms( header_info, from_server, error, sizeof(error))) ){
342		goto error;
343	}
344
345
346	buffer[0] = 0;
347	if( jobsize ){
348		if( (fd = Checkread(tempfile, &statb) ) < 0 ){
349			status = JFAIL;
350			SNPRINTF( error, sizeof(error))
351				"Do_secure_work: reopen of '%s' for read failed - %s",
352					tempfile, Errormsg(errno));
353			goto error;
354		}
355		status = Scan_block_file( fd, error, sizeof(error), header_info );
356		if( (fd = Checkwrite(tempfile,&statb,O_WRONLY|O_TRUNC,1,0)) < 0 ){
357			status = JFAIL;
358			SNPRINTF( error, sizeof(error))
359				"Do_secure_work: reopen of '%s' for write failed - %s",
360					tempfile, Errormsg(errno));
361			goto error;
362		}
363	} else {
364		if( (fd = Checkwrite(tempfile,&statb,O_WRONLY|O_TRUNC,1,0)) < 0 ){
365			status = JFAIL;
366			SNPRINTF( error, sizeof(error))
367				"Do_secure_work: reopen of '%s' for write failed - %s",
368					tempfile, Errormsg(errno));
369			goto error;
370		}
371		if( (s = Find_str_value(header_info,INPUT,Value_sep)) ){
372			Dispatch_input( &fd, s );
373		}
374	}
375
376 error:
377
378	if( fd >= 0 ) close(fd); fd = -1;
379	DEBUGF(DRECV1)("Do_secure_work: status %d, tempfile '%s', error '%s'",
380		status, tempfile, error );
381	if( error[0] ){
382		DEBUGF(DRECV1)("Do_secure_work: updating tempfile '%s', error '%s'",
383			tempfile, error );
384		if( (fd = Checkwrite(tempfile,&statb,O_WRONLY|O_TRUNC,1,0)) < 0 ){
385			Errorcode = JFAIL;
386			LOGERR_DIE(LOG_INFO) "Do_secure_work: reopen of '%s' for write failed",
387				tempfile );
388		}
389		Write_fd_str(fd,error);
390		close(fd);
391	}
392	DEBUGF(DRECV1)("Do_secure_work: returning %d", status );
393	return( status );
394}
395#endif
396
397/***************************************************************************
398 * void Fix_auth() - get the Use_auth_DYN value for the remote printer
399 ***************************************************************************/
400
401struct security *Fix_receive_auth( char *name, struct line_list *info )
402{
403	struct security *s;
404
405	if( name == 0 ){
406		if( Is_server ){
407			name = Auth_forward_DYN;
408		} else {
409			name = Auth_DYN;
410		}
411	}
412
413	for( s = SecuritySupported; s->name && Globmatch(s->name, name ); ++s );
414	DEBUG1("Fix_receive_auth: name '%s' matches '%s'", name, s->name );
415	if( s->name == 0 ){
416		s = 0;
417	} else {
418		char buffer[64], *str;
419		if( !(str = s->config_tag) ) str = s->name;
420		SNPRINTF(buffer,sizeof(buffer))"%s_", str );
421		Find_default_tags( info, Pc_var_list, buffer );
422		Find_tags( info, &Config_line_list, buffer );
423		Find_tags( info, &PC_entry_line_list, buffer );
424		Expand_hash_values( info );
425	}
426	if(DEBUGL1)Dump_line_list("Fix_receive_auth: info", info );
427	return(s);
428}
429
430
431int Check_secure_perms( struct line_list *options, int from_server,
432	char *error, int errlen )
433{
434	/*
435	 * line 1 - CLIENT=xxxx   - client authentication
436	 * line 2 - SERVER=xxxx   - server authentication
437	 * ...    - FROM=xxxx     - from
438	 * line 3 - INPUT=\00x  - command line
439	 */
440	char *authfrom, *authuser;
441	authfrom = Find_str_value(options,AUTHFROM,Value_sep);
442	if( !authfrom ) authfrom = Find_str_value(options,FROM,Value_sep);
443	authuser = Find_str_value(options,AUTHUSER,Value_sep);
444	if( !from_server ){
445		if( !authuser && authfrom ) authuser = authfrom;
446	}
447	if( !authuser ) authuser = Find_str_value(options,CLIENT,Value_sep);
448	Set_str_value(options, AUTHTYPE, Perm_check.authtype );
449	Set_str_value(options, AUTHFROM, authfrom );
450	Set_str_value(options, AUTHUSER, authuser );
451	Perm_check.authfrom = Find_str_value(options,AUTHFROM,Value_sep);
452	Perm_check.authuser = authuser = Find_str_value(options,AUTHUSER,Value_sep);
453	if( !authuser ){
454		SNPRINTF( error, errlen) "Printer %s@%s: missing authentication client id",
455			Printer_DYN,Report_server_as_DYN?Report_server_as_DYN:ShortHost_FQDN );
456		return( JABORT );
457	}
458	Perm_check.authca = Find_str_value(options,AUTHCA,Value_sep);
459	DEBUGFC(DRECV1)Dump_line_list("Check_secure_perms - after",options);
460	DEBUGFC(DRECV1)Dump_perm_check( "Check_secure_perms - checking", &Perm_check );
461	return(0);
462}
463