2016-01-12 17:17:22 +03:00
/*
* Unix SMB / CIFS implementation .
*
* CUPS printing backend helper to execute smbspool
*
* Copyright ( C ) 2010 - 2011 Andreas Schneider < asn @ samba . org >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 3 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
# include "includes.h"
# include "system/filesys.h"
# include "system/passwd.h"
# include <errno.h>
2016-04-27 19:01:51 +03:00
# include <stdlib.h>
2016-01-12 17:17:22 +03:00
# include <string.h>
# include <cups/backend.h>
# include "dynconfig/dynconfig.h"
2016-04-29 14:28:42 +03:00
# undef calloc
2016-01-12 17:17:22 +03:00
enum cups_smb_dbglvl_e {
CUPS_SMB_LOG_DEBUG = 0 ,
CUPS_SMB_LOG_ERROR ,
} ;
2017-11-19 21:34:58 +03:00
static void cups_smb_debug ( enum cups_smb_dbglvl_e lvl , const char * format , . . . )
PRINTF_ATTRIBUTE ( 2 , 3 ) ;
2016-01-12 17:17:22 +03:00
# define CUPS_SMB_DEBUG(...) cups_smb_debug(CUPS_SMB_LOG_DEBUG, __VA_ARGS__)
# define CUPS_SMB_ERROR(...) cups_smb_debug(CUPS_SMB_LOG_DEBUG, __VA_ARGS__)
static void cups_smb_debug ( enum cups_smb_dbglvl_e lvl , const char * format , . . . )
{
const char * prefix = " DEBUG " ;
char buffer [ 1024 ] ;
va_list va ;
va_start ( va , format ) ;
vsnprintf ( buffer , sizeof ( buffer ) , format , va ) ;
va_end ( va ) ;
switch ( lvl ) {
case CUPS_SMB_LOG_DEBUG :
prefix = " DEBUG " ;
break ;
case CUPS_SMB_LOG_ERROR :
prefix = " ERROR " ;
break ;
}
fprintf ( stderr ,
" %s: SMBSPOOL_KRB5 - %s \n " ,
prefix ,
buffer ) ;
}
/*
* This is a helper binary to execute smbspool .
*
* It needs to be installed or symlinked as :
* / usr / lib / cups / backend / smb
*
* The permissions of the binary need to be set to 0700 so that it is executed
* as root . The binary switches to the user which is passed via the environment
* variable AUTH_UID , so we can access the kerberos ticket .
*/
int main ( int argc , char * argv [ ] )
{
char smbspool_cmd [ PATH_MAX ] = { 0 } ;
struct passwd * pwd ;
2019-05-13 17:55:49 +03:00
struct group * g = NULL ;
2016-01-12 17:17:22 +03:00
char gen_cc [ PATH_MAX ] = { 0 } ;
struct stat sb ;
2019-03-12 11:40:58 +03:00
char * env = NULL ;
char auth_info_required [ 256 ] = { 0 } ;
char device_uri [ 4096 ] = { 0 } ;
2016-01-12 17:17:22 +03:00
uid_t uid = ( uid_t ) - 1 ;
gid_t gid = ( gid_t ) - 1 ;
2019-05-13 17:55:49 +03:00
gid_t groups [ 1 ] = { ( gid_t ) - 1 } ;
2016-01-12 17:17:22 +03:00
unsigned long tmp ;
int cmp ;
int rc ;
2019-03-12 11:40:58 +03:00
env = getenv ( " DEVICE_URI " ) ;
if ( env ! = NULL & & strlen ( env ) > 2 ) {
snprintf ( device_uri , sizeof ( device_uri ) , " %s " , env ) ;
}
2016-01-12 17:17:22 +03:00
/* Check if AuthInfoRequired is set to negotiate */
env = getenv ( " AUTH_INFO_REQUIRED " ) ;
2017-02-16 09:57:42 +03:00
/* If not set, then just call smbspool. */
2019-03-12 11:40:58 +03:00
if ( env = = NULL | | env [ 0 ] = = 0 ) {
2017-07-07 15:08:49 +03:00
CUPS_SMB_DEBUG ( " AUTH_INFO_REQUIRED is not set - "
" execute smbspool " ) ;
goto smbspool ;
2017-02-16 09:57:42 +03:00
} else {
2017-07-07 15:08:49 +03:00
CUPS_SMB_DEBUG ( " AUTH_INFO_REQUIRED=%s " , env ) ;
2019-03-12 11:40:58 +03:00
snprintf ( auth_info_required ,
sizeof ( auth_info_required ) ,
" %s " ,
env ) ;
2017-07-07 15:08:49 +03:00
cmp = strcmp ( env , " username,password " ) ;
if ( cmp = = 0 ) {
CUPS_SMB_DEBUG ( " Authenticate using username/password - "
" execute smbspool " ) ;
goto smbspool ;
}
/* if AUTH_INFO_REQUIRED=none */
cmp = strcmp ( env , " negotiate " ) ;
if ( cmp ! = 0 ) {
CUPS_SMB_ERROR ( " Authentication unsupported " ) ;
fprintf ( stderr , " ATTR: auth-info-required=negotiate \n " ) ;
return CUPS_BACKEND_AUTH_REQUIRED ;
}
2016-01-12 17:17:22 +03:00
}
2017-02-16 09:57:42 +03:00
uid = getuid ( ) ;
CUPS_SMB_DEBUG ( " Started with uid=%d \n " , uid ) ;
if ( uid ! = 0 ) {
goto smbspool ;
2016-01-12 17:17:22 +03:00
}
/*
* AUTH_UID gets only set if we have an incoming connection over the
* CUPS unix domain socket .
*/
env = getenv ( " AUTH_UID " ) ;
if ( env = = NULL ) {
CUPS_SMB_ERROR ( " AUTH_UID is not set " ) ;
fprintf ( stderr , " ATTR: auth-info-required=negotiate \n " ) ;
return CUPS_BACKEND_AUTH_REQUIRED ;
}
if ( strlen ( env ) > 10 ) {
CUPS_SMB_ERROR ( " Invalid AUTH_UID " ) ;
return CUPS_BACKEND_FAILED ;
}
errno = 0 ;
tmp = strtoul ( env , NULL , 10 ) ;
if ( errno ! = 0 | | tmp > = UINT32_MAX ) {
CUPS_SMB_ERROR ( " Failed to convert AUTH_UID=%s " , env ) ;
return CUPS_BACKEND_FAILED ;
}
uid = ( uid_t ) tmp ;
pwd = getpwuid ( uid ) ;
if ( pwd = = NULL ) {
CUPS_SMB_ERROR ( " Failed to find system user: %u - %s " ,
uid , strerror ( errno ) ) ;
return CUPS_BACKEND_FAILED ;
}
gid = pwd - > pw_gid ;
rc = setgroups ( 0 , NULL ) ;
if ( rc ! = 0 ) {
CUPS_SMB_ERROR ( " Failed to clear groups - %s " ,
strerror ( errno ) ) ;
return CUPS_BACKEND_FAILED ;
}
2019-05-13 17:55:49 +03:00
/*
* We need the primary group of the ' lp ' user . This is needed to access
* temporary files in / var / spool / cups / .
*/
g = getgrnam ( " lp " ) ;
if ( g = = NULL ) {
CUPS_SMB_ERROR ( " Failed to find user 'lp' - %s " ,
strerror ( errno ) ) ;
return CUPS_BACKEND_FAILED ;
}
CUPS_SMB_DEBUG ( " Adding group 'lp' (%u) " , g - > gr_gid ) ;
groups [ 0 ] = g - > gr_gid ;
rc = setgroups ( sizeof ( groups ) , groups ) ;
if ( rc ! = 0 ) {
CUPS_SMB_ERROR ( " Failed to set groups for 'lp' - %s " ,
strerror ( errno ) ) ;
return CUPS_BACKEND_FAILED ;
}
2016-01-12 17:17:22 +03:00
CUPS_SMB_DEBUG ( " Switching to gid=%d " , gid ) ;
rc = setgid ( gid ) ;
if ( rc ! = 0 ) {
2017-11-19 21:34:58 +03:00
CUPS_SMB_ERROR ( " Failed to switch to gid=%u - %s " ,
2016-01-12 17:17:22 +03:00
gid ,
strerror ( errno ) ) ;
return CUPS_BACKEND_FAILED ;
}
CUPS_SMB_DEBUG ( " Switching to uid=%u " , uid ) ;
rc = setuid ( uid ) ;
if ( rc ! = 0 ) {
2017-11-19 21:34:58 +03:00
CUPS_SMB_ERROR ( " Failed to switch to uid=%u - %s " ,
2016-01-12 17:17:22 +03:00
uid ,
strerror ( errno ) ) ;
return CUPS_BACKEND_FAILED ;
}
2017-07-12 17:07:25 +03:00
env = getenv ( " KRB5CCNAME " ) ;
if ( env ! = NULL & & env [ 0 ] ! = 0 ) {
snprintf ( gen_cc , sizeof ( gen_cc ) , " %s " , env ) ;
goto create_env ;
}
2016-01-12 17:17:22 +03:00
snprintf ( gen_cc , sizeof ( gen_cc ) , " /tmp/krb5cc_%d " , uid ) ;
rc = lstat ( gen_cc , & sb ) ;
if ( rc = = 0 ) {
snprintf ( gen_cc , sizeof ( gen_cc ) , " FILE:/tmp/krb5cc_%d " , uid ) ;
} else {
snprintf ( gen_cc , sizeof ( gen_cc ) , " /run/user/%d/krb5cc " , uid ) ;
rc = lstat ( gen_cc , & sb ) ;
if ( rc = = 0 & & S_ISDIR ( sb . st_mode ) ) {
snprintf ( gen_cc ,
sizeof ( gen_cc ) ,
" DIR:/run/user/%d/krb5cc " ,
uid ) ;
} else {
# if defined(__linux__)
snprintf ( gen_cc ,
sizeof ( gen_cc ) ,
" KEYRING:persistent:%d " ,
uid ) ;
# endif
}
}
2017-07-12 17:07:25 +03:00
create_env :
2016-01-12 17:17:22 +03:00
/*
* Make sure we do not have LD_PRELOAD or other security relevant
* environment variables set .
*/
2016-04-27 19:01:51 +03:00
# ifdef HAVE_CLEARENV
2016-01-12 17:17:22 +03:00
clearenv ( ) ;
2016-04-27 19:01:51 +03:00
# else
2016-04-29 14:28:42 +03:00
{
extern char * * environ ;
2019-03-12 11:40:58 +03:00
environ = calloc ( 3 , sizeof ( * environ ) ) ;
2016-04-29 14:28:42 +03:00
}
2016-04-27 19:01:51 +03:00
# endif
2016-01-12 17:17:22 +03:00
CUPS_SMB_DEBUG ( " Setting KRB5CCNAME to '%s' " , gen_cc ) ;
setenv ( " KRB5CCNAME " , gen_cc , 1 ) ;
2019-03-12 11:40:58 +03:00
if ( device_uri [ 0 ] ! = ' \0 ' ) {
setenv ( " DEVICE_URI " , device_uri , 1 ) ;
}
if ( auth_info_required [ 0 ] ! = ' \0 ' ) {
setenv ( " AUTH_INFO_REQUIRED " , auth_info_required , 1 ) ;
}
2016-01-12 17:17:22 +03:00
smbspool :
snprintf ( smbspool_cmd ,
sizeof ( smbspool_cmd ) ,
" %s/smbspool " ,
get_dyn_BINDIR ( ) ) ;
return execv ( smbspool_cmd , argv ) ;
}