/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <endian.h>
#include <errno.h>
#include <fcntl.h>
#include <pwd.h>
#include <security/_pam_macros.h>
#include <security/pam_misc.h>
#include <security/pam_modules.h>
#include <security/pam_modutil.h>
#include "alloc-util.h"
#include "audit-util.h"
#include "bus-common-errors.h"
#include "bus-error.h"
#include "bus-util.h"
#include "def.h"
#include "fd-util.h"
#include "fileio.h"
#include "formats-util.h"
#include "hostname-util.h"
#include "login-util.h"
#include "macro.h"
#include "parse-util.h"
#include "socket-util.h"
#include "strv.h"
#include "terminal-util.h"
#include "util.h"
static int parse_argv(
const char **class,
const char **type,
bool *debug) {
unsigned i;
for (i = 0; i < (unsigned) argc; i++) {
if (class)
if (type)
if (debug)
*debug = true;
int k;
if (k < 0)
else if (debug)
*debug = k;
} else
}
return 0;
}
static int get_user_data(
const char **ret_username,
int r;
if (r != PAM_SUCCESS) {
return r;
}
return PAM_AUTH_ERR;
}
if (!pw) {
return PAM_USER_UNKNOWN;
}
*ret_username = username;
return PAM_SUCCESS;
}
};
int v, r;
/* We deduce the X11 socket from the display name, then use
* SO_PEERCRED to determine the X11 server process, ask for
* the controlling tty of that and if it's a VC then we know
* the seat and the virtual terminal. Sounds ugly, is only
* semi-ugly. */
r = socket_from_display(display, &p);
if (r < 0)
return r;
if (fd < 0)
return -errno;
return -errno;
if (r < 0)
return r;
if (r < 0)
return r;
v = vtnr_from_tty(tty);
if (v < 0)
return v;
else if (v == 0)
return -ENOENT;
if (seat)
*seat = "seat0";
return 0;
}
static int export_legacy_dbus_address(
const char *runtime) {
_cleanup_free_ char *s = NULL;
int r = PAM_BUF_ERR;
if (is_kdbus_available()) {
goto error;
} else {
/* FIXME: We *really* should move the access() check into the
* daemons that spawn dbus-daemon, instead of forcing
* DBUS_SESSION_BUS_ADDRESS= here. */
if (!s)
goto error;
return PAM_SUCCESS;
s = mfree(s);
goto error;
}
if (r != PAM_SUCCESS)
goto error;
return PAM_SUCCESS;
return r;
}
int flags,
const char
/* Make this a NOP on non-logind systems */
if (!logind_running())
return PAM_SUCCESS;
if (parse_argv(handle,
&type_pam,
&debug) < 0)
return PAM_SESSION_ERR;
if (debug)
if (r != PAM_SUCCESS) {
return r;
}
/* Make sure we don't enter a loop by talking to
* systemd-logind when it is actually waiting for the
* background to finish start-up. If the service is
* "systemd-user" we simply set XDG_RUNTIME_DIR and
* leave. */
return PAM_BUF_ERR;
if (r != PAM_SUCCESS) {
return r;
}
if (r != PAM_SUCCESS)
return r;
return PAM_SUCCESS;
}
/* Otherwise, we ask logind to create a session for us */
/* A tty with a colon is usually an X11 display,
* placed there to show up in utmp. We rearrange
* things and don't pretend that an X display was a
* tty. */
/* cron has been setting PAM_TTY to "cron" for a very
* long time and it probably shouldn't stop doing that
* for compatibility reasons. */
type = "unspecified";
class = "background";
/* ssh has been setting PAM_TTY to "ssh" for a very
* long time and probably shouldn't stop doing that
* for compatibility reasons. */
type ="tty";
class = "user";
}
/* If this fails vtnr will be 0, that's intended */
}
vtnr = 0;
}
/* Talk to logind over the message bus */
r = sd_bus_open_system(&bus);
if (r < 0) {
return PAM_SESSION_ERR;
}
if (debug)
"uid="UID_FMT" pid="PID_FMT" service=%s type=%s class=%s desktop=%s seat=%s vtnr=%"PRIu32" tty=%s display=%s remote=%s remote_user=%s remote_host=%s",
r = sd_bus_call_method(bus,
"org.freedesktop.login1",
"/org/freedesktop/login1",
"org.freedesktop.login1.Manager",
"CreateSession",
&error,
&reply,
"uusssssussbssa(sv)",
type,
seat,
vtnr,
tty,
0);
if (r < 0) {
return PAM_SUCCESS;
} else {
return PAM_SYSTEM_ERR;
}
}
r = sd_bus_message_read(reply,
"soshusub",
&id,
&seat,
&vtnr,
&existing);
if (r < 0) {
return PAM_SESSION_ERR;
}
if (debug)
"id=%s object_path=%s runtime_path=%s session_fd=%d seat=%s vtnr=%u original_uid=%u",
if (r != PAM_SUCCESS) {
return r;
}
/* Don't set $XDG_RUNTIME_DIR if the user we now
* authenticated for does not match the original user
* of the session. We do this in order not to result
* in privileged apps clobbering the runtime directory
* unnecessarily. */
if (r != PAM_SUCCESS) {
return r;
}
if (r != PAM_SUCCESS)
return r;
}
if (r != PAM_SUCCESS) {
return r;
}
}
if (vtnr > 0) {
if (r != PAM_SUCCESS) {
return r;
}
}
if (r != PAM_SUCCESS) {
return r;
}
if (session_fd >= 0) {
if (session_fd < 0) {
return PAM_SESSION_ERR;
}
if (r != PAM_SUCCESS) {
return r;
}
}
return PAM_SUCCESS;
}
int flags,
const char *id;
int r;
/* Only release session if it wasn't pre-existing when we
* tried to create it */
/* Before we go and close the FIFO we need to tell
* logind that this is a clean session shutdown, so
* that it doesn't just go and slaughter us
* immediately after closing the fd */
r = sd_bus_open_system(&bus);
if (r < 0) {
return PAM_SESSION_ERR;
}
r = sd_bus_call_method(bus,
"org.freedesktop.login1",
"/org/freedesktop/login1",
"org.freedesktop.login1.Manager",
"ReleaseSession",
&error,
NULL,
"s",
id);
if (r < 0) {
return PAM_SESSION_ERR;
}
}
/* Note that we are knowingly leaking the FIFO fd here. This
* way, logind can watch us die. If we closed it here it would
* not have any clue when that is completed. Given that one
* cannot really have multiple PAM sessions open from the same
* process this means we will leak one FD at max. */
return PAM_SUCCESS;
}