pamsshagentauth-code/pam_user_key_allowed2.c

314 lines
10 KiB
C

/*
* Copyright (c) 2000 Markus Friedl. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Some miniscule portions Copyright, 2008 Jamie Beverly
* for pam_ssh_agent_auth PAM module
*
* Those portions retain the BSD style license of the original
*/
#include "includes.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <pwd.h>
#include <stdio.h>
#include <signal.h>
#include <errno.h>
#include <string.h>
#include "xmalloc.h"
#include "ssh.h"
#include "ssh2.h"
#include "buffer.h"
#include "log.h"
#include "compat.h"
#include "key.h"
#include "pathnames.h"
#include "misc.h"
#include "secure_filename.h"
#include "uidswap.h"
#include "identity.h"
/* return 1 if user allows given key */
/* Modified slightly from original found in auth2-pubkey.c */
static int
pamsshagentauth_check_authkeys_file(FILE * f, char *file, Key * key)
{
char line[SSH_MAX_PUBKEY_BYTES];
int found_key = 0;
u_long linenum = 0;
Key *found;
char *fp;
found_key = 0;
found = pamsshagentauth_key_new(key->type);
while(read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) {
char *cp = NULL; /* *key_options = NULL; */
/* Skip leading whitespace, empty and comment lines. */
for(cp = line; *cp == ' ' || *cp == '\t'; cp++);
if(!*cp || *cp == '\n' || *cp == '#')
continue;
if(pamsshagentauth_key_read(found, &cp) != 1) {
/* no key? check if there are options for this key */
int quoted = 0;
pamsshagentauth_verbose("user_key_allowed: check options: '%s'", cp);
/* key_options = cp; */
for(; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) {
if(*cp == '\\' && cp[1] == '"')
cp++; /* Skip both */
else if(*cp == '"')
quoted = !quoted;
}
/* Skip remaining whitespace. */
for(; *cp == ' ' || *cp == '\t'; cp++);
if(pamsshagentauth_key_read(found, &cp) != 1) {
pamsshagentauth_verbose("user_key_allowed: advance: '%s'", cp);
/* still no key? advance to next line */
continue;
}
}
if(pamsshagentauth_key_equal(found, key)) {
found_key = 1;
pamsshagentauth_logit("matching key found: file/command %s, line %lu", file,
linenum);
fp = pamsshagentauth_key_fingerprint(found, SSH_FP_MD5, SSH_FP_HEX);
pamsshagentauth_logit("Found matching %s key: %s",
pamsshagentauth_key_type(found), fp);
pamsshagentauth_xfree(fp);
break;
}
}
pamsshagentauth_key_free(found);
if(!found_key)
pamsshagentauth_verbose("key not found");
return found_key;
}
/*
* Checks whether key is allowed in file.
* returns 1 if the key is allowed or 0 otherwise.
*/
int
pamsshagentauth_user_key_allowed2(struct passwd *pw, Key * key, char *file)
{
FILE *f;
int found_key = 0;
struct stat st;
char buf[SSH_MAX_PUBKEY_BYTES];
/* Temporarily use the user's uid. */
pamsshagentauth_verbose("trying public key file %s", file);
/* Fail not so quietly if file does not exist */
if(stat(file, &st) < 0) {
pamsshagentauth_verbose("File not found: %s", file);
return 0;
}
/* Open the file containing the authorized keys. */
f = fopen(file, "r");
if(!f) {
return 0;
}
if(pamsshagentauth_secure_filename(f, file, pw, buf, sizeof(buf)) != 0) {
fclose(f);
pamsshagentauth_logit("Authentication refused: %s", buf);
return 0;
}
found_key = pamsshagentauth_check_authkeys_file(f, file, key);
fclose(f);
return found_key;
}
/*
* Checks whether key is allowed in output of command.
* returns 1 if the key is allowed or 0 otherwise.
*/
int
pamsshagentauth_user_key_command_allowed2(char *authorized_keys_command,
char *authorized_keys_command_user,
struct passwd *user_pw, Key * key)
{
FILE *f;
int ok, found_key = 0;
struct passwd *pw;
struct stat st;
int status, devnull, p[2], i;
pid_t pid;
char errmsg[512];
char username[512] = { 0 };
if(authorized_keys_command == NULL || authorized_keys_command[0] != '/')
return 0;
/* getpwnam of authorized_keys_command_user will overwrite the statics used by getpwnam (including pw_name) */
strncpy(username, user_pw->pw_name, sizeof(username) - 1);
/* If no user specified to run commands the default to target user */
if(authorized_keys_command_user == NULL) {
pw = user_pw;
}
else {
pw = getpwnam(authorized_keys_command_user);
if(pw == NULL) {
pamsshagentauth_logerror("authorized_keys_command_user \"%s\" not found: %s",
authorized_keys_command_user, strerror(errno));
return 0;
}
}
pamsshagentauth_temporarily_use_uid(pw);
if(stat(authorized_keys_command, &st) < 0) {
pamsshagentauth_logerror
("Could not stat AuthorizedKeysCommand \"%s\": %s",
authorized_keys_command, strerror(errno));
goto out;
}
if(pamsshagentauth_auth_secure_path
(authorized_keys_command, &st, NULL, 0, errmsg, sizeof(errmsg)) != 0) {
pamsshagentauth_logerror("Unsafe AuthorizedKeysCommand: %s", errmsg);
goto out;
}
/* open the pipe and read the keys */
if(pipe(p) != 0) {
pamsshagentauth_logerror("%s: pipe: %s", __func__, strerror(errno));
goto out;
}
pamsshagentauth_debug("Running AuthorizedKeysCommand: \"%s\" as \"%s\" with argument: \"%s\"",
authorized_keys_command, pw->pw_name, username);
/*
* Don't want to call this in the child, where it can fatal() and
* run cleanup_exit() code.
*/
pamsshagentauth_restore_uid();
switch ((pid = fork())) {
case -1: /* error */
pamsshagentauth_logerror("%s: fork: %s", __func__, strerror(errno));
close(p[0]);
close(p[1]);
return 0;
case 0: /* child */
for(i = 0; i < NSIG; i++)
signal(i, SIG_DFL);
/* do this before the setresuid so thta they can be logged */
if((devnull = open(_PATH_DEVNULL, O_RDWR)) == -1) {
pamsshagentauth_logerror("%s: open %s: %s", __func__, _PATH_DEVNULL,
strerror(errno));
_exit(1);
}
if(dup2(devnull, STDIN_FILENO) == -1 || dup2(p[1], STDOUT_FILENO) == -1
|| dup2(devnull, STDERR_FILENO) == -1) {
pamsshagentauth_logerror("%s: dup2: %s", __func__, strerror(errno));
_exit(1);
}
#if defined(HAVE_SETRESGID) && !defined(BROKEN_SETRESGID)
if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0) {
#else
if (setgid(pw->pw_gid) != 0 || setegid(pw->pw_gid) != 0) {
#endif
pamsshagentauth_logerror("setresgid %u: %s", (u_int) pw->pw_gid,
strerror(errno));
_exit(1);
}
#ifdef HAVE_SETRESUID
if(setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) != 0) {
#else
if (setuid(pw->pw_uid) != 0 || seteuid(pw->pw_uid) != 0) {
#endif
pamsshagentauth_logerror("setresuid %u: %s", (u_int) pw->pw_uid,
strerror(errno));
_exit(1);
}
close(p[0]);
closefrom(STDERR_FILENO + 1);
execl(authorized_keys_command, authorized_keys_command, username, (char *)NULL);
/* pretty sure this will barf because we are now suid, but since we
should't reach this anyway, I'll leave it here */
pamsshagentauth_logerror("AuthorizedKeysCommand %s exec failed: %s",
authorized_keys_command, strerror(errno));
_exit(127);
default: /* parent */
break;
}
pamsshagentauth_temporarily_use_uid(pw);
close(p[1]);
if((f = fdopen(p[0], "r")) == NULL) {
pamsshagentauth_logerror("%s: fdopen: %s", __func__, strerror(errno));
close(p[0]);
/* Don't leave zombie child */
while(waitpid(pid, NULL, 0) == -1 && errno == EINTR);
goto out;
}
ok = pamsshagentauth_check_authkeys_file(f, authorized_keys_command, key);
fclose(f);
while(waitpid(pid, &status, 0) == -1) {
if(errno != EINTR) {
pamsshagentauth_logerror("%s: waitpid: %s", __func__,
strerror(errno));
goto out;
}
}
if(WIFSIGNALED(status)) {
pamsshagentauth_logerror("AuthorizedKeysCommand %s exited on signal %d",
authorized_keys_command, WTERMSIG(status));
goto out;
} else if(WEXITSTATUS(status) != 0) {
pamsshagentauth_logerror("AuthorizedKeysCommand %s returned status %d",
authorized_keys_command, WEXITSTATUS(status));
goto out;
}
found_key = ok;
out:
pamsshagentauth_restore_uid();
return found_key;
}