aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am5
-rwxr-xr-xexamples/usergitconfig195
-rw-r--r--pam_ldaphome/pam_ldaphome.c160
3 files changed, 359 insertions, 1 deletions
diff --git a/Makefile.am b/Makefile.am
index e72f2c3..7a55296 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -48,7 +48,10 @@ SUBDIRS = \
$(GROUPMEMBER_DIR)\
pamck
-EXTRA_DIST=ChangeLog.svn
+EXTRA_DIST=ChangeLog.svn examples
+dist-hook:
+ rm -rf `find $(distdir)/examples -name '*~'`
+
# Name of the previous ChangeLog file.
prev_change_log = ChangeLog.svn
# Start Git ChangeLog from this date.
diff --git a/examples/usergitconfig b/examples/usergitconfig
new file mode 100755
index 0000000..ad2ab5e
--- /dev/null
+++ b/examples/usergitconfig
@@ -0,0 +1,195 @@
+#! /usr/bin/perl
+# This file is part of pam-modules.
+# Copyright (C) 2014 Sergey Poznyakoff
+#
+# 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, 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/>.
+
+use strict;
+use Net::LDAP;
+
+=head1 NAME
+
+usergitconfig - initialize user .gitconfig file
+
+=head1 SYNOPSIS
+
+B<usergitconfig> I<LOGIN>
+
+=head1 DESCRIPTION
+
+This program reads the B<.gitconfig> file from the current working directory
+and expands occurrences of B<${I<ATTRIBUTE>}> with the value of I<ATTRIBUTE>
+from the LDAP B<posixAccount> record for user I<LOGIN>. If there is no such
+attribute, an empty value is substituted.
+
+The program reads its configuration from one of the following files:
+B</etc/ldap.conf>, B</etc/ldap/ldap.conf> and B</etc/openldap/ldap.conf>.
+These files are tried in this order and the first one of them that exists
+is read.
+
+The following configuration statements are used:
+
+=over 4
+
+=item B<uri> B<ldap[si]://>[I<name>[:I<port>]] ...>
+
+Specifies the URI of the LDAP server (or servers) to connect to. The default
+is B<ldap://127.0.0.1>.
+
+=item B<base> I<DN>
+
+Specifies the default base DN to use when performing ldap operations.
+The base must be specified as a Distinguished Name in LDAP format.
+
+=item B<binddn> I<DN>
+
+Specifies the default bind DN to use.
+
+=item B<binddnpw> I<PASS>
+
+Specifies the password to use with B<binddn>.
+
+=item B<uid> I<ATTR>
+
+Name of the attribute to use instead of B<uid>. The LDAP record is searched
+using the filter B<(&(objectClass=posixAccount)(I<ATTR>=I<LOGIN>))>.
+
+=back
+
+All keywords are case-insensitive.
+
+By default, the program expands the keywords in the B<.gitconfig> file. This
+can be changed via the environment variable B<GITCONFIG_TEMPLATE>. If it is
+set, the program uses its value as the name of the file to use. This file is
+read line by line, expanding the keywords encountered and the resulting text
+is written to B<.gitconfig>.
+
+The program is intended to be used in B<initrc-command> keyword of the
+B<pam_ldaphome>(8) configuration file.
+
+=head1 SEE ALSO
+
+B<pam_ldaphome>(8), B<ldap.conf>(5).
+
+=head1 AUTHOR
+
+Sergey Poznyakoff <gray@gnu.org>
+
+=cut
+
+# ###################################
+# Configuration file handling
+# ###################################
+
+my %config = ('uri' => 'ldap://127.0.0.1', 'uid' => 'uid');
+
+sub read_config_file($) {
+ my $config_file = shift;
+ my $file;
+ my $line = 0;
+
+ open($file, "<", $config_file) or die("cannot open $config_file: $!");
+ while (<$file>) {
+ ++$line;
+ chomp;
+ s/^\s+//;
+ s/\s+$//;
+ s/#.*//;
+ next if ($_ eq "");
+ my @kwp = split(/\s*\s+\s*/, $_, 2);
+ $config{lc($kwp[0])} = $kwp[1];
+ }
+ close($file);
+}
+
+# ###################################
+# LDAP Functions
+# ###################################
+
+sub assert {
+ my $mesg = shift;
+ my $action = shift;
+ die("An error occurred $action: ".$mesg->error) if ($mesg->code);
+ return $mesg;
+}
+
+sub ldap_connect {
+ my $ldap = Net::LDAP->new($config{'uri'})
+ or die("Unable to connect to LDAP server $config{'uri'}: $!");
+ my @bindargs = ();
+ if (defined($config{'binddn'})) {
+ push(@bindargs, $config{'binddn'});
+ push(@bindargs, password => $config{'bindpass'}) # FIXME
+ if defined($config{'bindpass'});
+ }
+ assert($ldap->bind(@bindargs), "binding to the server");
+ return $ldap;
+}
+
+# ###################################
+# MAIN
+# ###################################
+
+die "bad number of arguments" unless ($#ARGV == 0);
+
+## Read configuration
+foreach my $file ("/etc/ldap.conf", "/etc/ldap/ldap.conf",
+ "/etc/openldap/ldap.conf") {
+ if (-e $file) {
+ read_config_file($file);
+ last;
+ }
+}
+
+my $ldap = ldap_connect;
+
+my $filter = "(&(objectClass=posixAccount)($config{'uid'}=$ARGV[0]))";
+
+my $res = assert($ldap->search(base => $config{'base'},
+ filter => $filter),
+ "searching for $filter in $config{'base'}");
+my $entry = $res->entry(0);
+
+my $template = defined($ENV{'GITCONFIG_TEMPLATE'})
+ ? $ENV{'GITCONFIG_TEMPLATE'}
+ : ".gitconfig";
+open(my $fd, "<", $template) or die "can't open $template: $!";
+
+my $outfile;
+if ($template eq ".gitconfig") {
+ $outfile = ".gitconfig.$$";
+} else {
+ $outfile = ".gitconfig";
+}
+
+open(my $ofd, ">", $outfile) or die "can't open $outfile for output: $!";
+
+while (<$fd>) {
+ s/\$\{(.*?)\}/$entry->get_value($1)/gex;
+ print $ofd $_;
+}
+
+close $fd;
+close $ofd;
+
+if ($outfile ne ".gitconfig") {
+ unlink(".gitconfig")
+ or die "can't unlink .gitconfig: $1";
+ rename($outfile, ".gitconfig")
+ or die "can't rename $outfile to .gitconfig: $1";
+}
+
+$ldap->unbind;
+
+
diff --git a/pam_ldaphome/pam_ldaphome.c b/pam_ldaphome/pam_ldaphome.c
index b83063a..f3191b9 100644
--- a/pam_ldaphome/pam_ldaphome.c
+++ b/pam_ldaphome/pam_ldaphome.c
@@ -30,6 +30,9 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/wait.h>
#include <ldap.h>
#include <pwd.h>
#include <grp.h>
@@ -1429,6 +1432,160 @@ create_home_dir(pam_handle_t *pamh, struct passwd *pw, struct gray_env *env)
return create_ok;
}
+static int
+run_prog(pam_handle_t *pamh, struct passwd *pw, struct gray_env *env,
+ const char *command, const char *logfile)
+{
+ pid_t pid, rc;
+ int p[2];
+ long ttl;
+ time_t start;
+ int i, status;
+ struct timeval tv;
+ unsigned long timeout_option = 10;
+
+ DEBUG(2,("running command %s", command));
+ get_intval(env, "exec-timeout", 10, &timeout_option);
+
+ if (pipe(p)) {
+ _pam_log(LOG_ERR, "pipe: %s", strerror(errno));
+ return PAM_SYSTEM_ERR;
+ }
+
+ pid = fork();
+ if (pid == -1) {
+ close(p[0]);
+ close(p[1]);
+ _pam_log(LOG_ERR, "fork: %s", strerror(errno));
+ return PAM_SYSTEM_ERR;
+ }
+
+ if (pid == 0) {
+ /* child */
+ char *argv[3];
+
+ if (chdir(pw->pw_dir)) {
+ _pam_log(LOG_ERR, "chdir: %s", strerror(errno));
+ _exit(127);
+ }
+
+ if (dup2(p[1], 1) == -1) {
+ _pam_log(LOG_ERR, "dup2: %s", strerror(errno));
+ _exit(127);
+ }
+ for (i = sysconf(_SC_OPEN_MAX); i >= 0; i--) {
+ if (i != 1)
+ close(i);
+ }
+ open("/dev/null", O_RDONLY);
+ if (logfile) {
+ if (open(logfile, O_CREAT|O_APPEND|O_WRONLY,
+ 0644) == -1) {
+ _pam_log(LOG_ERR, "open(%s): %s",
+ logfile, strerror(errno));
+ _exit(127);
+ }
+ } else
+ dup2(1, 2);
+ argv[0] = (char*) command;
+ argv[1] = pw->pw_name;
+ argv[2] = NULL;
+ execv(command, argv);
+ _exit(127);
+ }
+
+ /* master */
+ close(p[1]);
+
+ start = time(NULL);
+ while (1) {
+ ttl = timeout_option - (time(NULL) - start);
+ if (ttl <= 0) {
+ _pam_log(LOG_ERR, "timed out waiting for %s", command);
+ break;
+ }
+ tv.tv_sec = ttl;
+ tv.tv_usec = 0;
+ rc = select(0, NULL, NULL, NULL, &tv);
+ if (rc == -1 && errno == EINTR) {
+ rc = waitpid(pid, &status, WNOHANG);
+ if (rc == pid)
+ break;
+ if (rc == (pid_t)-1) {
+ _pam_log(LOG_ERR, "waitpid: %s",
+ strerror(errno));
+ break;
+ }
+ }
+ }
+
+ close(p[0]);
+
+ if (rc != pid) {
+ _pam_log(LOG_NOTICE, "killing %s (pid %lu)",
+ command, (unsigned long) pid);
+ kill(pid, SIGKILL);
+
+ while ((rc = waitpid(pid, &status, 0)) == -1 &&
+ errno == EINTR);
+ if (rc == (pid_t)-1) {
+ _pam_log(LOG_ERR, "waitpid: %s", strerror(errno));
+ return PAM_SYSTEM_ERR;
+ }
+ } else if (WIFEXITED(status)) {
+ status = WEXITSTATUS(status);
+ if (status) {
+ _pam_log(LOG_ERR, "%s exited with status %d",
+ command, status);
+ return PAM_SYSTEM_ERR;
+ } else
+ DEBUG(2,("%s finished successfully", command));
+ } else if (WIFSIGNALED(status)) {
+ status = WTERMSIG(status);
+ _pam_log(LOG_ERR, "%s got signal %d", command, status);
+ return PAM_SYSTEM_ERR;
+ } else if (status) {
+ _pam_log(LOG_ERR, "%s failed: unknown status 0x%x",
+ command, status);
+ return PAM_SYSTEM_ERR;
+ }
+ return PAM_SUCCESS;
+}
+
+void
+sigchld(int sig)
+{
+ /* nothing */;
+}
+
+static int
+run_initrc(pam_handle_t *pamh, struct passwd *pw, struct gray_env *env)
+{
+ int rc;
+ struct sigaction sa, save_sa;
+ const char *command = gray_env_get(env, "initrc-command");
+ const char *logfile = gray_env_get(env, "initrc-log");
+
+ if (!command)
+ return PAM_SUCCESS;
+
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ sa.sa_handler = sigchld;
+ if (sigaction(SIGCHLD, &sa, &save_sa)) {
+ _pam_log(LOG_ERR, "sigaction: %m");
+ return PAM_SYSTEM_ERR;
+ }
+
+ rc = run_prog(pamh, pw, env, command, logfile);
+
+ if (sigaction(SIGCHLD, &save_sa, NULL)) {
+ _pam_log(LOG_ERR, "sigaction failed to restore SIGCHLD: %m");
+ return PAM_SYSTEM_ERR;
+ }
+ return rc;
+}
+
PAM_EXTERN int
pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv)
{
@@ -1449,6 +1606,9 @@ pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv)
if (check_user_groups(pamh, env, &pw, &retval) == 0) {
switch (create_home_dir(pamh, pw, env)) {
case create_ok:
+ retval = run_initrc(pamh, pw, env);
+ if (retval)
+ break;
retval = import_public_key(pamh, pw, env);
break;
case create_failure:

Return to:

Send suggestions and report system problems to the System administrator.