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