1
0
mirror of https://github.com/samba-team/samba.git synced 2024-12-23 17:34:34 +03:00
samba-mirror/source3/client/smbspool_krb5_wrapper.c

281 lines
6.6 KiB
C
Raw Normal View History

/*
* 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>
#include <stdlib.h>
#include <string.h>
#include <cups/backend.h>
#include "dynconfig/dynconfig.h"
#undef calloc
enum cups_smb_dbglvl_e {
CUPS_SMB_LOG_DEBUG = 0,
CUPS_SMB_LOG_ERROR,
};
static void cups_smb_debug(enum cups_smb_dbglvl_e lvl, const char *format, ...)
PRINTF_ATTRIBUTE(2, 3);
#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;
struct group *g = NULL;
char gen_cc[PATH_MAX] = {0};
struct stat sb;
char *env = NULL;
char auth_info_required[256] = {0};
char device_uri[4096] = {0};
uid_t uid = (uid_t)-1;
gid_t gid = (gid_t)-1;
gid_t groups[1] = { (gid_t)-1 };
unsigned long tmp;
int cmp;
int rc;
env = getenv("DEVICE_URI");
if (env != NULL && strlen(env) > 2) {
snprintf(device_uri, sizeof(device_uri), "%s", env);
}
/* Check if AuthInfoRequired is set to negotiate */
env = getenv("AUTH_INFO_REQUIRED");
/* If not set, then just call smbspool. */
if (env == NULL || env[0] == 0) {
CUPS_SMB_DEBUG("AUTH_INFO_REQUIRED is not set - "
"execute smbspool");
goto smbspool;
} else {
CUPS_SMB_DEBUG("AUTH_INFO_REQUIRED=%s", env);
snprintf(auth_info_required,
sizeof(auth_info_required),
"%s",
env);
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;
}
}
uid = getuid();
CUPS_SMB_DEBUG("Started with uid=%d\n", uid);
if (uid != 0) {
goto smbspool;
}
/*
* 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;
}
/*
* 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;
}
CUPS_SMB_DEBUG("Switching to gid=%d", gid);
rc = setgid(gid);
if (rc != 0) {
CUPS_SMB_ERROR("Failed to switch to gid=%u - %s",
gid,
strerror(errno));
return CUPS_BACKEND_FAILED;
}
CUPS_SMB_DEBUG("Switching to uid=%u", uid);
rc = setuid(uid);
if (rc != 0) {
CUPS_SMB_ERROR("Failed to switch to uid=%u - %s",
uid,
strerror(errno));
return CUPS_BACKEND_FAILED;
}
env = getenv("KRB5CCNAME");
if (env != NULL && env[0] != 0) {
snprintf(gen_cc, sizeof(gen_cc), "%s", env);
goto create_env;
}
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
}
}
create_env:
/*
* Make sure we do not have LD_PRELOAD or other security relevant
* environment variables set.
*/
#ifdef HAVE_CLEARENV
clearenv();
#else
{
extern char **environ;
environ = calloc(3, sizeof(*environ));
}
#endif
CUPS_SMB_DEBUG("Setting KRB5CCNAME to '%s'", gen_cc);
setenv("KRB5CCNAME", gen_cc, 1);
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);
}
smbspool:
snprintf(smbspool_cmd,
sizeof(smbspool_cmd),
"%s/smbspool",
get_dyn_BINDIR());
return execv(smbspool_cmd, argv);
}