New utility "ldappubkey".

* NEWS: Document new features.
* doc/pam-modules.texi: Document usergitconfig.
* doc/pam_ldaphome.8in: Update.
* examples/ldappubkey: New file.
* examples/usergitconfig: Refer to perldoc on error.
* lib/env.c (gray_env_get_bool): New function.
* lib/graypam.h (gray_env_get_bool): New proto.
* pam_ldaphome/pam_ldaphome.c (import_public_key): Do nothing if
"import-public-keys" is set to "no".
(pam_sm_authenticate): New function.
This commit is contained in:
Sergey Poznyakoff 2014-05-19 13:49:03 +03:00
parent 867eaccec7
commit 7636fa3e7b
8 changed files with 289 additions and 10 deletions

11
NEWS
View file

@ -1,4 +1,4 @@
pam-modules -- history of user-visible changes. 2014-05-06
pam-modules -- history of user-visible changes. 2014-05-11
Copyright (C) 2001, 2004-2005, 2007-2012, 2014 Sergey Poznyakoff
See the end of file for copying conditions.
@ -11,6 +11,15 @@ Version 1.8.92, (Git)
Tests whether the user is a member of one or more groups.
* pam_ldaphome can invoke an external program
An external program defined with the inirc-command keyword is run
in the newly created user's home directory. It can be used for
per-user customization of the files copied from the skeleton dir.
The examples directory contains a perl program "usergitconfig", which,
when used as inirc-command, initializes the user's .gitconfig file.
* Bugfixes

View file

@ -1249,6 +1249,12 @@ Sets the LDAP filter expression to return a user profile. The
filters as defined in RFC 4515.
@end deffn
@deffn {pam_ldaphome config} import-public-keys bool
When set to @samp{no}, disables importing public keys from LDAP. You
may wish to use this option if you are using @command{openssh} 6.1 or
later with @command{ldappubkey} as @samp{AuthorizedKeysCommand}.
@end deffn
@deffn {pam_ldaphome config} pubkey-attr text
Defines the name of the attribute which holds the user public key.
@end deffn
@ -1351,6 +1357,7 @@ which case the usual shell dequoting rules apply.
@menu
* ldaphome example::
* usergitconfig::
@end menu
@node ldaphome example
@ -1437,6 +1444,43 @@ passwd: files ldap
group: files ldap
@end example
@node usergitconfig
@section usergitconfig
@cindex usergitconfig
The @file{examples} subdirectory of the @command{pam-modules}
distribution contains a program @command{usergitconfig} which
is designed to customize user's @file{.gitconfig} file using
attributes from his LDAP entry.
The command reads the @file{.gitconfig} file and replaces any
occurrence of @samp{$@{@var{attr}@}} with the value of the LDAP
attribute @var{attr}. Not defined attributes are replaced with
empty strings.
To use this utility with @command{pam_ldaphome}, copy it to
some location of preference (say, @file{/usr/libexec}), and
add the following to @command{pam_ldaphome} configuration file:
@example
skel /etc/skel
initrc-command /usr/libexec/usergitconfig
@end example
The @file{/etc/skel} directory should contain the file @file{.gitconfig}.
Suppose its contents is as follows:
@example
[user]
name = $@{cn@}
email = $@{mail@}
@end example
@noindent
Then, after successful completion of @command{pam_ldaphome}, the
user's @file{.gitconfig} file will contain his real name and email
set properly from the database.
@node umotd
@chapter pam_umotd
@set MODULE pam_umotd

View file

@ -14,7 +14,7 @@
.\" You should have received a copy of the GNU General Public License
.\" along with PAM-Modules. If not, see <http://www.gnu.org/licenses/>.
.so config.so
.TH PAM_LDAPHOME 8 "April 4, 2014" "PAM-MODULES" "Pam-Modules User Reference"
.TH PAM_LDAPHOME 8 "May 19, 2014" "PAM-MODULES" "Pam-Modules User Reference"
.SH NAME
pam_ldaphome \- create and populate user home directories
.SH SYNOPSIS
@ -108,6 +108,11 @@ Defines a LDAP filter expression which returns the user profile. The
\fIEXPR\fR should conform to the string representation for search
filters as defined in RFC 4515.
.TP
.BI import\-public\-keys " BOOL"
When set to \fBno\fR, disables importing public keys from LDAP. You
may wish to use this option if you are using \fBopenssh\fR 6.1 or
later with \fBldappubkey\fR as \fBAuthorizedKeysCommand\fR.
.TP
.BI pubkey\-attr " TEXT"
Defines the name of the attribute that keeps user's public SSH key.
.TP
@ -203,7 +208,8 @@ the \fB\-\-enable\-debug\fR option.
\fBaudit\fR
Log auditing information.
.SH MODULE TYPES PROVIDED
.BR auth .
.BR auth ,
.BR session .
.SH RETURN VALUES
.TP
.B PAM_SUCCESS

176
examples/ldappubkey Executable file
View file

@ -0,0 +1,176 @@
#! /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
ldappubkey - get user public ssh keys from the LDAP database
=head1 SYNOPSIS
B<ldappubkey> I<LOGIN>
=head1 DESCRIPTION
Produces on the standard output public ssh keys for the user I<LOGIN>, each
on a separate line. The program is designed for use with B<sshd>(8) version
6.x or higher. Public keys are obtained from a LDAP database. The
configuration is looked up in 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 (all keywords are
case-insensitive):
=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<bindpw> 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>))>.
=item B<publickeyattribute> I<ATTR>
Name of the attribute which holds the public key. Default is B<grayPublicKey>.
=back
=head1 OPTIONS
=over 4
=item B<-h>
Show program usage.
=item B<--help>
Show detailed help page.
=back
=head1 SEE ALSO
B<sshd>(8), B<sshd_config>(5), B<ldap.conf>(5).
=head1 BUGS
LDAP filter string is hardcoded.
=head1 AUTHOR
Sergey Poznyakoff <gray@gnu.org>
=cut
# ###################################
# Configuration file handling
# ###################################
my %config = ('uri' => 'ldap://127.0.0.1', 'uid' => 'uid',
'publickeyattribute' => 'grayPublicKey');
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);
}
sub assert {
my $mesg = shift;
my $action = shift;
die("An error occurred $action: ".$mesg->error) if ($mesg->code);
return $mesg;
}
# ###################################
# MAIN
# ###################################
die "bad number of arguments; try perldoc $0 for more info"
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 = 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{'bindpw'})
if defined($config{'bindpw'});
}
assert($ldap->bind(@bindargs), "binding to the server");
my $attr = $config{'publickeyattribute'};
my $filter = "(&(objectClass=posixAccount)($config{'uid'}=$ARGV[0]))";
my $res = assert($ldap->search(base => $config{'base'},
filter => $filter,
attr => [ $attr ] ),
"searching for $filter in $config{'base'}");
foreach my $entry ($res->entry(0)) {
my $keyref = $entry->get_value($attr, asref => 1);
for (@{$keyref}) {
print "$_\n";
}
}
# END

View file

@ -78,6 +78,13 @@ 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.
Example B<.gitconfig> file:
[user]
name = ${cn}
email = ${mail}
=head1 SEE ALSO
B<pam_ldaphome>(8), B<ldap.conf>(5).
@ -141,7 +148,8 @@ sub ldap_connect {
# MAIN
# ###################################
die "bad number of arguments" unless ($#ARGV == 0);
die "bad number of arguments; try perldoc $0 for more info"
unless ($#ARGV == 0);
## Read configuration
foreach my $file ("/etc/ldap.conf", "/etc/ldap/ldap.conf",

View file

@ -25,6 +25,15 @@ gray_env_get(struct gray_env *env, const char *name)
return NULL;
}
int
gray_env_get_bool(struct gray_env *env, const char *name, int dfl)
{
char *p = gray_env_get(env, name);
if (!p)
return dfl;
return gray_boolean_true_p(p);
}
void
gray_env_free(struct gray_env *env)
{

View file

@ -209,6 +209,7 @@ struct gray_env {
};
char *gray_env_get(struct gray_env *env, const char *name);
int gray_env_get_bool(struct gray_env *env, const char *name, int dfl);
void gray_env_free(struct gray_env *env);
int gray_env_read(const char *file_name, struct gray_env **penv);

View file

@ -1321,6 +1321,9 @@ import_public_key(pam_handle_t *pamh, struct passwd *pw, struct gray_env *env)
const char *filter_pat = gray_env_get(env, "filter");
const char *attr = gray_env_get(env, "pubkey-attr");
if (!gray_env_get_bool(env, "import-public-keys", 1))
return PAM_SUCCESS;
if (!filter_pat) {
_pam_log(LOG_ERR, "configuration variable `filter' not set");
return PAM_SERVICE_ERR;
@ -1855,15 +1858,16 @@ run_initrc(pam_handle_t *pamh, struct passwd *pw, struct gray_env *env)
return rc;
}
PAM_EXTERN int
pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv)
static int
ldaphome_main(pam_handle_t *pamh, int flags, int argc, const char **argv,
const char *func)
{
int retval = PAM_AUTH_ERR;
struct gray_env *env;
_pam_parse(pamh, argc, argv);
DEBUG(90,("enter pam_sm_authenticate"));
DEBUG(90,("enter %s", func));
gray_pam_init(PAM_AUTHINFO_UNAVAIL);
if (gray_env_read(config_file_name, &env) == 0) {
char *val;
@ -1889,10 +1893,18 @@ pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv)
}
gray_env_free(env);
}
DEBUG(90,("exit pam_sm_authenticate: %d", retval));
DEBUG(90,("exit %s: %d", func, retval));
return retval;
}
PAM_EXTERN int
pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv)
{
return ldaphome_main(pamh, flags, argc, argv, __FUNCTION__);
}
PAM_EXTERN int
pam_sm_setcred(pam_handle_t *pamh,
int flags,
@ -1902,6 +1914,20 @@ pam_sm_setcred(pam_handle_t *pamh,
return PAM_SUCCESS;
}
PAM_EXTERN int
pam_sm_open_session (pam_handle_t *pamh, int flags, int argc,
const char **argv)
{
return ldaphome_main(pamh, flags, argc, argv, __FUNCTION__);
}
PAM_EXTERN int
pam_sm_close_session (pam_handle_t *pamh, int flags, int argc,
const char **argv)
{
return PAM_SUCCESS;
}
#ifdef PAM_STATIC
struct pam_module _pam_ldaphome_modstruct = {
@ -1909,8 +1935,8 @@ struct pam_module _pam_ldaphome_modstruct = {
pam_sm_authenticate,
pam_sm_setcred,
NULL,
NULL,
NULL,
pam_sm_open_session,
pam_sm_close_session,
NULL
};