/*
* Copyright © 2012 Canonical Ltd.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3, as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranties of
* MERCHANTABILITY, SATISFACTORY QUALITY, 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 .
*
* Author: Mike Gabriel
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "pam-x2go-children.h"
#include "auth-check-path.h"
void
pam_sm_authenticate_helper (int *stdinpipe, const char* username, const char* rhost, const char* ruser, const char* rdomain)
{
dup2(stdinpipe[0], 0);
char * args[5];
args[0] = (char *)auth_check_path;
args[1] = (char *)rhost;
args[2] = (char *)ruser;
args[3] = (char *)rdomain;
args[4] = NULL;
struct passwd * pwdent = getpwnam(username);
if (pwdent == NULL) {
_exit(EXIT_FAILURE);
}
/* Setting groups, but allowing EPERM as if we're not 100% root
we might not be able to do this */
if (setgroups(1, &pwdent->pw_gid) != 0 && errno != EPERM) {
_exit(EXIT_FAILURE);
}
if (setgid(pwdent->pw_gid) < 0 || setuid(pwdent->pw_uid) < 0 ||
setegid(pwdent->pw_gid) < 0 || seteuid(pwdent->pw_uid) < 0) {
_exit(EXIT_FAILURE);
}
if (clearenv() != 0) {
_exit(EXIT_FAILURE);
}
if (chdir(pwdent->pw_dir) != 0) {
_exit(EXIT_FAILURE);
}
setenv("HOME", pwdent->pw_dir, 1);
execvp(args[0], args);
_exit(0);
}
int
session_socket_handler (struct passwd * pwdent, int readypipe, const char * ruser, const char * rhost, const char * rdomain, const char * password)
{
/* Socket stuff */
int socketfd = 0;
struct sockaddr_un socket_addr;
/* Connected user */
socklen_t connected_addr_size;
int connectfd = 0;
struct sockaddr_un connected_addr;
/* Our buffer */
char * buffer = NULL;
int buffer_len = 0;
int buffer_fill = 0;
/* Track write out */
int writedata = 0;
/* Track ready writing */
int readywrite = 0;
/* Setting groups, but allowing EPERM as if we're not 100% root
we might not be able to do this */
if (setgroups(1, &pwdent->pw_gid) != 0 && errno != EPERM) {
_exit(EXIT_FAILURE);
}
if (setgid(pwdent->pw_gid) < 0 || setuid(pwdent->pw_uid) < 0 ||
setegid(pwdent->pw_gid) < 0 || seteuid(pwdent->pw_uid) < 0) {
/* Don't need to clean up yet */
return EXIT_FAILURE;
}
if (clearenv() != 0) {
/* Don't need to clean up yet */
return EXIT_FAILURE;
}
if (chdir(pwdent->pw_dir) != 0) {
/* Don't need to clean up yet */
return EXIT_FAILURE;
}
if (rdomain[0] == '\0') {
rdomain = ".";
}
/* Build this up as a buffer so we can just write it and see that
very, very clearly */
buffer_len += strlen(ruser) + 1; /* Add one for the space */
buffer_len += strlen(rhost) + 1; /* Add one for the space */
buffer_len += strlen(rdomain) + 1; /* Add one for the space */
buffer_len += strlen(password) + 1; /* Add one for the NULL */
if (buffer_len < 5) {
/* Don't need to clean up yet */
return EXIT_FAILURE;
}
buffer = malloc(buffer_len);
if (buffer == NULL) {
/* Don't need to clean up yet */
return EXIT_FAILURE;
}
/* Lock the buffer before writing */
if (mlock(buffer, buffer_len) != 0) {
/* We can't lock, we go home */
goto cleanup;
}
buffer_fill = snprintf(buffer, buffer_len, "%s %s %s %s", ruser, password, rhost, rdomain);
if (buffer_fill > buffer_len) {
/* This really shouldn't happen, but if for some reason we have an
difference between they way that the lengths are calculated we want
to catch that. */
goto cleanup;
}
/* Make our socket and bind it */
socketfd = socket(AF_UNIX, SOCK_STREAM, 0);
if (socketfd < 0) {
goto cleanup;
}
memset(&socket_addr, 0, sizeof(struct sockaddr_un));
socket_addr.sun_family = AF_UNIX;
strncpy(socket_addr.sun_path, pwdent->pw_dir, sizeof(socket_addr.sun_path) - 1);
strncpy(socket_addr.sun_path + strlen(pwdent->pw_dir), "/.x2go-socket", (sizeof(socket_addr.sun_path) - strlen(pwdent->pw_dir)) - 1);
/* We bind the socket before forking so that we ensure that
there isn't a race condition to get to it. Things will block
otherwise. */
if (bind(socketfd, (struct sockaddr *)&socket_addr, sizeof(struct sockaddr_un)) < 0) {
goto cleanup;
}
/* Set the socket file permissions to be 600 and the user and group
to be the guest user. NOTE: This won't protect on BSD */
if (chmod(socket_addr.sun_path, S_IRUSR | S_IWUSR) != 0 ||
chown(socket_addr.sun_path, pwdent->pw_uid, pwdent->pw_gid) != 0) {
goto cleanup;
}
if (listen(socketfd, 1) < 0) {
goto cleanup;
}
readywrite = write(readypipe, ALL_GOOD_SIGNAL, strlen(ALL_GOOD_SIGNAL) + 1);
if (readywrite != strlen(ALL_GOOD_SIGNAL) + 1) {
goto cleanup;
}
connected_addr_size = sizeof(struct sockaddr_un);
connectfd = accept(socketfd, (struct sockaddr *)&connected_addr, &connected_addr_size);
if (connectfd < 0) {
goto cleanup;
}
writedata = write(connectfd, buffer, buffer_len);
cleanup:
if (socketfd != 0) {
close(socketfd);
}
if (connectfd != 0) {
close(connectfd);
}
if (buffer != NULL) {
memset(buffer, 0, buffer_len);
munlock(buffer, buffer_len);
free(buffer);
buffer = NULL;
}
/* This should be only true on the write, so we can use this to check
out as writedata is init to 0 */
if (writedata == buffer_len) {
_exit (0);
}
_exit(EXIT_FAILURE);
}