1 /***************************************************************************
2  * LPRng - An Extended Print Spooler System
3  *
4  * Copyright 1988-2003, Patrick Powell, San Diego, CA
5  *     papowell@lprng.com
6  * See LICENSE for conditions of use.
7  *
8  ***************************************************************************/
9 
10 #include "lp.h"
11 #include "user_auth.h"
12 #include "sendjob.h"
13 #include "permission.h"
14 #include "getqueue.h"
15 #include "errorcodes.h"
16 #include "linksupport.h"
17 #include "krb5_auth.h"
18 #include "fileopen.h"
19 #include "child.h"
20 #include "gethostinfo.h"
21 #include "sendauth.h"
22 /**** ENDINCLUDE ****/
23 
24 /***************************************************************************
25  * Commentary:
26  * Patrick Powell Mon Apr 17 05:43:48 PDT 1995
27  *
28  * The protocol used to send a secure job consists of the following
29  * following:
30  *
31  * Client                                   Server
32  * \REQ_SECUREprintername C/F user\n - receive a command
33  *             0           1   2
34  * \REQ_SECUREprintername C/F user controlfile\n - receive a job
35  *             0           1   2
36  *
37  * 1. Get a temporary file
38  * 2. Generate the compressed data files - this has the format
39  *      Authentication
40  *      \n
41  *      \3count cfname\n
42  *      [count control file bytes]
43  *      \4count dfname\n
44  *      [count data file bytes]
45  *
46  * 3. send the \REQ_SECRemotePrinter_DYN user@RemoteHost_DYN file size\n
47  *    string to the remote RemoteHost_DYN, wait for an ACK
48  *
49  * 4. send the compressed data files - this has the format
50  *      wait for an ACK
51  ***************************************************************************/
52 
53 static void Put_in_auth( int tempfd, const char *key, char *value );
54 
55 /*
56  * Send_auth_transfer
57  *  1. we send the command line and wait for ACK of 0
58  *  \REQ_SEQUREprinter C/F sender_id authtype [jobsize]
59  *  2. if authtype == kerberos we do kerberos
60  *      - send a file to the remote end
61  *      - get back a file
62  *  3. if authtype == pgp we do pgp
63  *      - same as kerberos
64  *  3. if otherwise,  we start a process with command line options
65  *       fd 0 -  sock
66  *       fd 1 -  for reports
67  *       fd 2 -  for errors
68  *    /filter -C -P printer -n sender_id -A authtype -R remote_id -Ttempfile
69  *    The tempfile will be sent to the remote end and status
70  *     written back on fd 2
71  *     - we save this information
72  *     - reopen the file and put error messages in it.
73  *  RETURN:
74  *     0 - no error
75  *     !=0 - error
76  */
77 
Send_auth_transfer(int * sock,int transfer_timeout,struct job * job,struct job * logjob,char * error,int errlen,char * cmd,const struct security * security,struct line_list * info)78 int Send_auth_transfer( int *sock, int transfer_timeout,
79 	struct job *job, struct job *logjob, char *error, int errlen, char *cmd,
80 	const struct security *security, struct line_list *info )
81 {
82 	struct stat statb;
83 	int ack, len, n, fd;		/* ACME! The best... */
84 	int status = JFAIL;			/* job status */
85 	char *secure, *destination, *from, *client, *s;
86 	char *tempfile;
87 	char buffer[SMALLBUFFER];
88 	errno = 0;
89 
90 	secure = 0;
91 	fd = Make_temp_fd(&tempfile);
92 
93 	if( cmd && (s = safestrrchr(cmd,'\n')) ) *s = 0;
94 	DEBUG1("Send_auth_transfer: cmd '%s'", cmd );
95 
96 	if(DEBUGL1)Dump_line_list("Send_auth_transfer: info ", info );
97 
98 	destination = Find_str_value(info, DESTINATION );
99 	from = Find_str_value(info, FROM );
100 	client = Find_str_value(info, CLIENT );
101 
102 	if( safestrcmp(security->config_tag, "kerberos") ){
103 		Put_in_auth(fd,DESTINATION,destination);
104 		if( Is_server ) Put_in_auth(fd,SERVER,from);
105 		Put_in_auth(fd,CLIENT,client);
106 		if( cmd ){
107 			Put_in_auth(fd,INPUT,cmd);
108 		}
109 	} else {
110 		if( cmd && (Write_fd_str(fd,cmd) < 0 || Write_fd_str(fd,"\n") < 0) ){
111 			plp_snprintf(error, errlen, "Send_auth_transfer: '%s' write failed - %s",
112 				tempfile, Errormsg(errno) );
113 			goto error;
114 		}
115 		if( Is_server && (Write_fd_str(fd,client) < 0 || Write_fd_str(fd,"\n") < 0) ){
116 			plp_snprintf(error, errlen, "Send_auth_transfer: '%s' write failed - %s",
117 				tempfile, Errormsg(errno) );
118 			goto error;
119 		}
120 	}
121 
122 	if( Write_fd_str(fd,"\n") < 0 ){
123 		plp_snprintf(error, errlen, "Send_auth_transfer: '%s' write failed - %s",
124 			tempfile, Errormsg(errno) );
125 		goto error;
126 	}
127 
128 	s = Find_str_value(info, CMD );
129 	if( job ){
130         status = Send_normal( &fd, job, logjob, transfer_timeout, fd, 0);
131         if( status ) return( status );
132 		errno = 0;
133 		if( stat(tempfile,&statb) ){
134 			Errorcode = JABORT;
135 			logerr_die(LOG_INFO, "Send_auth_transfer: stat '%s' failed",
136 				tempfile);
137 		}
138 		plp_snprintf( buffer,sizeof(buffer), " %0.0f",(double)(statb.st_size) );
139 		secure = safestrdup3(s,buffer,"\n",__FILE__,__LINE__);
140 	} else {
141 		secure = safestrdup2(s,"\n",__FILE__,__LINE__);
142 	}
143 	close( fd ); fd = -1;
144 
145 	/* send the message */
146 	DEBUG3("Send_auth_transfer: sending '%s'", secure );
147 	status = Link_send( RemoteHost_DYN, sock, transfer_timeout,
148 		secure, safestrlen(secure), &ack );
149 	DEBUG3("Send_auth_transfer: status '%s'", Link_err_str(status) );
150 	if( status ){
151 		/* open output file */
152 		if( (fd = Checkwrite(tempfile,&statb,O_WRONLY|O_TRUNC,1,0)) < 0){
153 			Errorcode = JABORT;
154 			logerr_die(LOG_INFO, "Send_auth_transfer: open '%s' for write failed",
155 				tempfile);
156 		}
157 		/* we turn off IO from the socket */
158 		shutdown(*sock,1);
159 		if( (s = safestrchr(secure,'\n')) ) *s = 0;
160 		plp_snprintf( error, errlen,
161 			"error '%s' sending '%s' to %s@%s\n",
162 			Link_err_str(status), secure, RemotePrinter_DYN, RemoteHost_DYN );
163 		Write_fd_str( fd, error );
164 		error[0] = 0;
165 		DEBUG2("Send_auth_transfer: starting read");
166 		len = 0;
167 		while( (n = Read_fd_len_timeout(Send_query_rw_timeout_DYN, *sock,buffer+len,sizeof(buffer)-1-len)) > 0 ){
168 			buffer[n+len] = 0;
169 			DEBUG4("Send_auth_transfer: read '%s'", buffer);
170 			while( (s = strchr(buffer,'\n')) ){
171 				*s++ = 0;
172 				DEBUG2("Send_auth_transfer: doing '%s'", buffer);
173 				plp_snprintf(error,errlen, "%s\n", buffer );
174 				if( Write_fd_str(fd,error) < 0 ){
175 					Errorcode = JABORT;
176 					logerr(LOG_INFO, "Send_auth_transfer: write '%s' failed",
177 						tempfile );
178 					goto error;
179 				}
180 				memmove(buffer,s,safestrlen(s)+1);
181 			}
182 			len = safestrlen(buffer);
183 		}
184 		if( buffer[0] ){
185 			DEBUG2("Send_auth_transfer: doing '%s'", buffer);
186 			plp_snprintf(error,errlen, "%s\n", buffer );
187 			if( Write_fd_str(fd,error) < 0 ){
188 				Errorcode = JABORT;
189 				logerr(LOG_INFO, "Send_auth_transfer: write '%s' failed",
190 					tempfile );
191 				goto error;
192 			}
193 		}
194 
195 		close( fd ); fd = -1;
196 		error[0] = 0;
197 		goto error;
198 	}
199 
200 	/*
201      * now we do the protocol dependent exchange
202      */
203 
204 	status = security->client_send( sock, transfer_timeout, tempfile,
205 		error, errlen, security, info );
206 
207  error:
208 
209 	DEBUG3("Send_auth_transfer: sock %d, exit status %d, error '%s'",
210 		*sock, status, error );
211 	/* we are going to put the returned error status in the temp file
212 	 * as the device to read from
213 	 */
214 	if( secure ) free(secure); secure = 0;
215 	if( error[0] ){
216 		if( job ){
217 			setstatus(logjob, "Send_auth_transfer: %s", error );
218 			Set_str_value(&job->info,ERROR,error);
219 			Set_nz_flag_value(&job->info,ERROR_TIME,time(0));
220 		}
221 		if( (fd = Checkwrite(tempfile,&statb,O_WRONLY|O_TRUNC,1,0)) < 0){
222 			Errorcode = JFAIL;
223 			logerr_die(LOG_INFO, "Send_auth_transfer: cannot open '%s'", tempfile );
224 		}
225 		Write_fd_str(fd,error);
226 		close( fd ); fd = -1;
227 		error[0] = 0;
228 	}
229 	if( *sock >= 0 ){
230 		if( (fd = Checkread(tempfile,&statb)) < 0 ){
231 			Errorcode = JFAIL;
232 			logerr_die(LOG_INFO, "Send_auth_transfer: cannot open '%s'", tempfile );
233 		}
234 		if( dup2( fd, *sock ) == -1 ){
235 			Errorcode = JFAIL;
236 			logerr_die(LOG_INFO, "Send_auth_transfer: dup2(%d,%d)", fd, *sock );
237 		}
238 		if( fd != *sock ) close(fd); fd = -1;
239 	}
240 	Free_line_list(info);
241 	DEBUG3("Send_auth_transfer: exit status %d, error '%s'",
242 		status, error );
243 	return( status );
244 }
245 
246 /***************************************************************************
247  *
248  * struct security *Fix_send_auth( char *name, struct line_list *info
249  * 	char *error, int errlen )
250  *
251  * Find the information about the encrypt type and then make up the string
252  * to send to the server requesting the encryption
253  **************************************************************************/
254 
Fix_send_auth(char * name,struct line_list * info,struct job * job,char * error,int errlen)255 const struct security *Fix_send_auth( char *name, struct line_list *info,
256 	struct job *job, char *error, int errlen )
257 {
258 	const struct security *security = 0;
259 	char buffer[SMALLBUFFER], *from, *client, *destination;
260 	const char *tag, *server_tag, *key;
261 
262 	if( name == 0 ){
263 		if( Is_server ){
264 			name = Auth_forward_DYN;
265 		} else {
266 			name = Auth_DYN;
267 		}
268 	}
269 	DEBUG1("Fix_send_auth: name '%s'", name );
270 	if( name ){
271 		security = FindSecurity(name);
272 		if( !security ){
273 			plp_snprintf(error, errlen,
274 				"Fix_send_auth: '%s' security not supported", name );
275 			goto error;
276 		} else {
277 			DEBUG1("Fix_send_auth: name '%s' matches '%s'", name, security->name );
278 		}
279 	} else {
280 		DEBUG1("Fix_send_auth: no security" );
281 		return( 0 );
282 	}
283 
284 	/* check to see if we use unix_socket */
285 	if( security->auth_flags & IP_SOCKET_ONLY ){
286 		Set_DYN( &Unix_socket_path_DYN, 0 );
287 	}
288 
289 	if( !(tag = security->config_tag) ) tag = security->name;
290 	plp_snprintf(buffer,sizeof(buffer), "%s_", tag );
291 	Find_default_tags( info, Pc_var_list, buffer );
292 	Find_tags( info, &Config_line_list, buffer );
293 	Find_tags( info, &PC_entry_line_list, buffer );
294 	Expand_hash_values( info );
295 	if(DEBUGL1)Dump_line_list("Fix_send_auth: found info", info );
296 
297 	if( !(tag = security->config_tag) ) tag = security->name;
298 	if( !(server_tag = security->server_tag) ) server_tag = tag;
299 	if( Is_server ){
300 		/* forwarding */
301 		key = "F";
302 		from = Find_str_value(info,ID);
303 		if(!from)from = Find_str_value(info,"server_principal");
304 		if( from == 0 && safestrcmp(tag,"kerberos") && safestrcmp(tag,"none") ){
305 			plp_snprintf(error, errlen,
306 			"Fix_send_auth: '%s' security missing '%s_id' info", tag, tag );
307 			goto error;
308 		}
309 		Set_str_value(info,FROM,from);
310 		if( job ){
311 			client = Find_str_value(&job->info,AUTHUSER);
312 			Set_str_value(info,CLIENT,client);
313 		} else {
314 			client = (char *)Perm_check.authuser;
315 		}
316 		if( client == 0
317 			&& !(client = Find_str_value(info,"default_client_name"))
318 			&& safestrcmp(tag,"none") ){
319 			plp_snprintf(error, errlen,
320 			"Fix_send_auth: security '%s' missing authenticated client", tag );
321 			goto error;
322 		}
323 		Set_str_value(info,CLIENT,client);
324 		destination = Find_str_value(info,FORWARD_ID);
325 		if(!destination)destination = Find_str_value(info,"forward_principal");
326 		if( destination == 0 && safestrcmp(tag, "kerberos")
327 			&& safestrcmp(tag, "none")){
328 			plp_snprintf(error, errlen,
329 			"Fix_send_auth: '%s' security missing '%s_forward_id' info", tag, tag );
330 			goto error;
331 		}
332 	} else {
333 		/* from client */
334 		key = "C";
335 		from = Logname_DYN;
336 		Set_str_value(info,FROM,from);
337 		client = Logname_DYN;
338 		Set_str_value(info,CLIENT,client);
339 		destination = Find_str_value(info,ID);
340 		if(!destination)destination = Find_str_value(info,"server_principal");
341 		if( destination == 0 && safestrcmp(tag, "kerberos")
342 			&& safestrcmp(tag, "none") ){
343 			plp_snprintf(error, errlen,
344 			"Fix_send_auth: '%s' security missing destination '%s_id' info", tag, tag );
345 			goto error;
346 		}
347 	}
348 
349 	Set_str_value(info,DESTINATION,destination);
350 
351 	DEBUG1("Fix_send_auth: pr '%s', key '%s', from '%s', name '%s', tag '%s'",
352 		RemotePrinter_DYN,key, from, server_tag, tag);
353 	plp_snprintf( buffer, sizeof(buffer),
354 		"%c%s %s %s %s",
355 		REQ_SECURE,RemotePrinter_DYN,key, from, server_tag );
356 	Set_str_value(info,CMD,buffer);
357 	DEBUG1("Fix_send_auth: sending '%s'", buffer );
358 
359  error:
360 	if( error[0] ) security = 0;
361 	DEBUG1("Fix_send_auth: error '%s'", error );
362 	if(DEBUGL1)Dump_line_list("Fix_send_auth: info", info );
363 
364 	return(security);
365 }
366 
Put_in_auth(int tempfd,const char * key,char * value)367 void Put_in_auth( int tempfd, const char *key, char *value )
368 {
369 	char *v = Escape(value,1);
370 	DEBUG1("Put_in_auth: fd %d, key '%s' value '%s', v '%s'",
371 		tempfd, key, value, v );
372 	if(
373 		Write_fd_str(tempfd,key) < 0
374 		|| Write_fd_str(tempfd,"=") < 0
375 		|| Write_fd_str(tempfd,v) < 0
376 		|| Write_fd_str(tempfd,"\n") < 0
377 		){
378 		Errorcode = JFAIL;
379 		logerr_die(LOG_INFO, "Put_in_auth: cannot write to file" );
380 	}
381 	if( v ) free(v); v = 0;
382 }
383