August 17th, 2010 — Solaris
The second part of this article is here, so if you missed the first one, you might take a look Part One.
- Extending Schema
- LDAP support for sudo
- LDAP setup
- Setting up /etc/ldap.conf and nsswitch.conf
3. LDAP setup
Let’s guess your root suffix is dc=company,dc=com , you need to append the next entry to your directory :
dn: ou=sudoers,dc=company,dc=com
objectClass: top
objectClass: organizationalunit
description: Sudo Configuration
ou: sudoers
Besides we will need a default profile:
dn: cn=defaults,ou=sudoers,dc=company,dc=com
sudoOption: ignore_local_sudoers
objectClass: top
objectClass: sudoRole
cn: defaults
description: Our default options
sudooption: log_host
sudooption: logfile=/var/log/sudolog
sudooption: !syslog
Perhaps you would like to get the most of sudo’s powder, take a look in its website. You can add as much profiles as you like, suppose you want to add one for system administration:
dn: cn=sysadmin,ou=sudoers,dc=company,dc=com
objectClass: top
objectClass: sudoRole
cn: unix_admins
sudoUser: tuxman
sudoUser: darkman
sudoUser: bill
sudoHost: ALL
sudoCommand: /usr/bin/ls
As far as I concern, how to configure sudo is out of this post, however together the source of sudo there is an utility, sudoers2ldif, a perl script that helps you to translate your sudo’s configuration file. Next step requires to modify our profile. Probably you will have a similar profile to this one:
dn: cn=default,ou=profile,dc=company,dc=com
objectClass: DUAConfigProfile
defaultSearchBase: dc=company,dc=com
cn: default
credentialLevel: proxy
defaultServerList: 192.168.76.66
profileTTL: 300
searchTimeLimit: 60
authenticationMethod: simple
serviceSearchDescriptor: passwd:cn=sudoers,dc=company,dc=com
After these modifications you must initialize your client (ldapclient).
4. Setting up /etc/ldap.conf and nsswitch.conf
It’s time to tell our client where to find sudoers file, by means of /etc/ldap.conf, that looks something like this.
uri ldap://192.168.76.66
sudoers_base ou=sudoers,dc=company,dc=com
bindpw cn=proxyagent,ou=profile,dc=company,dc=com
binddn password
sudoers_debug 0
You might use anonymous access, that’s your choice, just remember to check your ACI’s. Pretty interesting the option sudoers_debug which helps you to debug, at level 3 will show you as much information as possible. The last step, how to find our sudoers’ profile, nsswitch.conf
sudoers: ldap
Let’s check if is working:
tuxman@host:$> sudo ls
[sudo] password for client:
sudo ls
LDAP Config Summary
===================
uri ldap://192.168.76.66
ldap_version 3
sudoers_base ou=sudoers,dc=company,dc=com
binddn (anonymous)
bindpw (anonymous)
ssl (no)
===================
sudo: ldap_initialize(ld, ldap://192.168.76.66)
sudo: ldap_set_option: debug -> 0
sudo: ldap_set_option: ldap_version -> 3
sudo: ldap_sasl_bind_s() ok
sudo: found:cn=defaults,ou=sudoers,dc=company,dc=com
sudo: ldap sudoOption: 'ignore_local_sudoers'
sudo: ldap sudoOption: 'log_host'
sudo: ldap sudoOption: 'logfile=/var/log/sudolog'
sudo: ldap sudoOption: '!syslog'
sudo: ldap search '(|(sudoUser=tuxman)(sudoUser=%other)(sudoUser=ALL))'
sudo: found:cn=sysadmin,ou=sudoers,dc=company,dc=com
sudo: ldap sudoHost 'ALL' ... MATCH!
sudo: ldap sudoCommand '/usr/bin/ls' ... MATCH!
sudo: Command allowed
sudo: user_matches=1
sudo: host_matches=1
sudo: sudo_ldap_lookup(0)=0x02
tuxman@host:$> files/ sudoers2ldif.pl
At this point everything should be working. Last step, to translate our sudoers file.
August 1st, 2010 — Solaris
Last day at work I had to get working sudo and ldap. I’m not gonna get into a discussion about if it’s worth or not to use sudo. I can just say from my own experience, if you have a large number of users and hosts, they are clearly distinguishable and you are using roles (i.e: sysadmin, backup, any kind of group…) it’s totally worth.
Moreover, take into account you would have to update every sudo config file , definitely would be tedious, and here’s when LDAP gets in.
I brought into play two virtual machines, both of them running Solaris 10. Commonly I prefer doing that before making some huge mistake in a real environment, so here’s what I did. I called box0 to the client and ldapbox to the server. The rest of the post, I’ll assume you have set up a Directory Server and Native LDAP client service working fine. Summary :
- Extending Schema
- LDAP support for sudo
- Structure of LDAP
- Setting up /etc/ldap.conf and nsswitch.conf
1. Extending Schema
This step is pretty straightforward, you only need to add the schema to your directory instance, and restart the server. Assuming your instance might be in the default path /var/opt/SUNWdsee/dsins1/config/schema/99users.ldif.
attributeTypes: ( 1.3.6.1.4.1.15953.9.1.1 NAME ‘sudoUser’ DESC ‘User(s) who may run sudo’ EQUALITY caseExactIA5Match SUBSTR caseExactIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 X-ORIGIN ‘SUDO’ )
attributeTypes: ( 1.3.6.1.4.1.15953.9.1.2 NAME ‘sudoHost’ DESC ‘Host(s) who may run sudo’ EQUALITY caseExactIA5Match SUBSTR caseExactIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 X-ORIGIN ‘SUDO’ )
attributeTypes: ( 1.3.6.1.4.1.15953.9.1.3 NAME ‘sudoCommand’ DESC ‘Command(s) to be executed by sudo’ EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 X-ORIGIN ‘SUDO’ )
attributeTypes: ( 1.3.6.1.4.1.15953.9.1.4 NAME ‘sudoRunAs’ DESC ‘User(s) impersonated by sudo’ EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 X-ORIGIN ‘SUDO’ )
attributeTypes: ( 1.3.6.1.4.1.15953.9.1.5 NAME ‘sudoOption’ DESC ‘Options(s) followed by sudo’ EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 X-ORIGIN ‘SUDO’ )
objectClasses: ( 1.3.6.1.4.1.15953.9.2.1 NAME ‘sudoRole’ SUP top STRUCTURAL DESC ‘Sudoer Entries’ MUST ( cn ) MAY ( sudoUser $ sudoHost $ sudoCommand $ sudoRunAs $ sudoOption $ description ) X-ORIGIN ‘SUDO’ )
Now you only need to restart the server.Why do I need to restart my server? The matter is, the first time you started your server, the file was read into memory, so any change you make later will not have effect, at least you restart the instance.
2. LDAP support for sudo
You will need to get the source code of sudo and at least 1.7 version or upper. The reason is because earlier versions will not read nsswitch.conf. I used sudo-1.7.2p7, you can get it from Sunfreeware. Of course if you want to compile you will need to solve some dependencies, here is the list, however you had better confirm by yourself.
/—|(Sudo)
|-* gcc-3.4.6-sol10-x86-local
|-* libiconv-1.13.1-sol10-x86-local
|-* libintl-3.4.0-sol10-x86-local
|-* openssl-1.0.0a-sol10-x86-local
/—|( OpenLdap )
|- * db-4.7.25.NC-sol10-x86-local
|- * libtool-2.2.6b-sol10-x86-local
|- * sasl-2.1.21-sol10-x86-local
|- * openldap-2.4.22-sol10-x86-local
At this point and after installing all the dependencies we just need to compile:
./configure --with-ldap && make
Those people who like tinkering with Unix tools, is time to call ldd and take a look into sudo.
libpam.so.1 => /lib/libpam.so.1
libdl.so.1 => /lib/libdl.so.1
* libldap-2.4.so.2 => (/usr/local/lib/libldap-2.4.so.2)
* liblber-2.4.so.2 => (/usr/local/lib/liblber-2.4.so.2)
libintl.so.8 => /usr/local/lib/libintl.so.8
libsocket.so.1 => /lib/libsocket.so.1
libnsl.so.1 => /lib/libnsl.so.1
libc.so.1 => /lib/libc.so.1
libcmd.so.1 => /lib/libcmd.so.1
libresolv.so.2 => /usr/lib/libresolv.so.2
libgen.so.1 => /usr/lib/libgen.so.1
libsasl2.so.2 => /usr/local/lib/libsasl2.so.2
libssl.so.1.0.0 => /usr/local/ssl/lib/libssl.so.1.0.0
libcrypto.so.1.0.0 => /usr/local/ssl/lib/libcrypto.so.1.0.0
libgcc_s.so.1 => /usr/local/lib/libgcc_s.so.1
libiconv.so.2 => /usr/local/lib/libiconv.so.2
libsec.so.1 => /usr/lib/libsec.so.1
libmp.so.2 => /lib/libmp.so.2
libmd.so.1 => /lib/libmd.so.1
libscf.so.1 => /lib/libscf.so.1
libavl.so.1 => /lib/libavl.so.1
libdoor.so.1 => /lib/libdoor.so.1
libuutil.so.1 => /lib/libuutil.so.1
libm.so.2 => /lib/libm.so.2
If the LDAP libraries does not appear remember to add the path:
# crle -l -u PATH_TO_LIBRARIES
Obviously I wouldn’t like having to install OpenLdap in all my clients ( if you want to apply to more than one ), so I thought to carry just with the libraries I needed. In a nutshell, we have just extended the schema and also enabled ldap support for sudo. The two last points for the next post.
June 11th, 2009 — HP-UX
There is a good explanation about how to use pam_authz. Basically, in HP-UX systems if you have initialized a client against a LDAP server, and you’d like following server policies instead of local policies, you should keep on reading this article.
Let’s go to the issue!!
- File /etc/opt/ldapux/pam_authz.policy.master
This file is read by the script, which looks for the variable $[GTIMEZULU] and substitutes for a timestamp.
# pam_authz.policy.master
deny:ldap_filter:(accountUnlockTime>=$[GTIMEZULU])
# Allow all users other than those denied above
allow:other
In my case, I only needed to check if the user was locked or not.
Inside the indicated document previously there is a bash script that solves that issue, I just decided try with Perl.
- The script
use English;
use strict;
use warnings;
use Fcntl qw(:flock :seek);
my $time="";
my $template="/etc/opt/ldapux/pam_authz.policy.master";
my $policy="/etc/opt/ldapux/pam_authz.policy";
# It should appear a system call and create the file referenced in $policy but
# this plugin does not seem to be working out.
&test_last_command();
chmod(0700,"$policy");
&update_time();
sub test_last_command(){
if ( $? != 0){
print "Error creating file\n";
exit -1;
}
}
sub update_time(){
my $line="";
open POLICY_FILE, ">$policy" or die $!;
while(1){
open TEMPLATE_FILE, "<$template" or die $!;
flock(POLICY_FILE, LOCK_EX);
seek(POLICY_FILE,0,SEEK_SET);
$time=`TZ=UTC;date "+%Y%m%d%H%M%SZ"`;
chop($time);
while(<TEMPLATE_FILE>){
$line=$_;
$line=~ s/\$\[GTIMEZULU\]/$time/g;
print POLICY_FILE $line;
}
sleep 2;
close TEMPLATE_FILE;
}
}
There was some strange problem with the highlight syntax plugin and I could not add a line. However, you can download the script directly.
May 31st, 2009 — Solaris
There is a PAM module available for creating home directories on fly, and this module is pam_mkhomedir.so. This is quite useful if you have a LDAP server ( in this case Directory Server 6.3) and you are inserting users but their home directories were not created. To my surprise, this module it was not available for Solaris, but I found an interesting link with useful information about this.
First of all , you’ll need to download the files Linux-PAM-80. For compiling this I did a little script, after reading the above article:
#!/bin/bash
PATH=/usr/sfw/bin:/usr/ccs/bin:$PATH;export PATH
gcc -c -g -O2 -D_REENTRANT -DPAM_DYNAMIC -Wall
-fPIC -I../../libpam/include \
-I../../libpamc/include \
-I../pammodutil/include pam_mkhomedir.c
After compiling the module was not working, so What should I do now? Well, I tried to debug why the module was not working, somehow I tried differents methods, the first I actived debug mode in syslog daemon, you only need to add
*.debug /var/adm/pam_log
in the /etc/syslog.conf . But here is what I found:
ay 18 10:27:25 des-to16-d sshd[26177]: [ID 547715 auth.debug] PAM[26177]: load_function: successful load of pam_sm_setcred
May 18 10:27:25 des-to16-d sshd[26177]: [ID 482737 auth.debug] PAM[26177]: pam_open_session(8a828, 0)
May 18 10:27:25 des-to16-d sshd[26177]: [ID 926797 auth.debug] PAM[26177]: load_modules(8a828, pam_sm_open_session)=/usr/lib/security/pam_mkhomedir.so
Nothing special that points me out how to solve this, so I tried another way. I tried to log with a ldap’s user, first with ssh but I was always getting out from the system, so that something interesting and annoying was happening. I thought to use telnet and I got this:
login: user1
Password:
ld.so.1: login: fatal: relocation error: file /usr/lib/security/pam_mkhomedir.so: symbol _pammodutil_getpwnam: referenced symbol not found
Connection to localhost closed by foreign host.
That told me more!! I opened pam_mkhomedir.c and I found the name of four functions :
_pammodutil_getpwnam
_pammodutil_read
_pammodutil_write
_pammodutil_cleanup
that there are no available in Solaris 10 ( of course neither above versions ). What did I do? I put all these functions together in the same file, and I added some includes, so this is all the code you need to add pam_mkhomedir.c before compiling:
/***** These includes are needed ********/
#include <security/pammodutil.h>
#include <errno.h>
#include <limits.h>
/*********** Functions ***************/
void _pammodutil_cleanup(pam_handle_t *pamh, void *data, int error_status)
{
if (data) {
/* junk it */
(void) free(data);
}
}
int _pammodutil_read(int fd, char *buffer, int count)
{
int block, offset = 0;
while (count > 0) {
block = read(fd, &amp;amp;amp;buffer[offset], count);
if (block < 0) {
if (errno == EINTR) continue;
return block;
}
if (block == 0) return offset;
offset += block;
count -= block;
}
return offset;
}
int _pammodutil_write(int fd, const char *buffer, int count)
{
int block, offset = 0;
while (count > 0) {
block = write(fd, &amp;amp;amp;buffer[offset], count);
if (block < 0) {
if (errno == EINTR) continue;
return block;
}
if (block == 0) return offset;
offset += block;
count -= block;
}
return offset;
}
struct passwd *_pammodutil_getpwnam(pam_handle_t *pamh, const char *user)
{
#ifdef HAVE_GETPWNAM_R
void *buffer=NULL;
size_t length = PWD_INITIAL_LENGTH;
do {
int status;
void *new_buffer;
struct passwd *result = NULL;
new_buffer = realloc(buffer, sizeof(struct passwd) + length);
if (new_buffer == NULL) {
D(("out of memory"));
/* no memory for the user - so delete the memory */
if (buffer) {
free(buffer);
}
return NULL;
}
buffer = new_buffer;
/* make the re-entrant call to get the pwd structure */
errno = 0;
status = getpwnam_r(user, buffer,
sizeof(struct passwd) + (char *) buffer,
length, &amp;amp;amp;result);
if (!status &amp;amp;amp;&amp;amp;amp; (result == buffer)) {
char *data_name;
const void *ignore;
int i;
data_name = malloc(strlen("_pammodutil_getpwnam") + 1 +
strlen(user) + 1 + intlen(INT_MAX) + 1);
if ((pamh != NULL) &amp;amp;amp;&amp;amp;amp; (data_name == NULL)) {
D(("was unable to register the data item [%s]",
pam_strerror(pamh, status)));
free(buffer);
return NULL;
}
if (pamh != NULL) {
for (i = 0; i < INT_MAX; i++) {
sprintf(data_name, "_pammodutil_getpwnam_%s_%d", user, i);
_pammodutil_lock();
status = PAM_NO_MODULE_DATA;
if (pam_get_data(pamh, data_name, &amp;amp;amp;ignore) != PAM_SUCCESS) {
status = pam_set_data(pamh, data_name,
result, _pammodutil_cleanup);
}
_pammodutil_unlock();
if (status == PAM_SUCCESS) {
break;
}
}
} else {
status = PAM_SUCCESS;
}
free(data_name);
if (status == PAM_SUCCESS) {
D(("success"));
return result;
}
D(("was unable to register the data item [%s]",
pam_strerror(pamh, status)));
free(buffer);
return NULL;
} else if (errno != ERANGE &amp;amp;amp;amp;amp;&amp;amp;amp;amp;amp; errno != EINTR) {
/* no sense in repeating the call */
break;
}
length <<= 2;
} while (length < PWD_ABSURD_PWD_LENGTH);
D(("pwd structure took %u bytes or so of memory",
length+sizeof(struct passwd)));
free(buffer);
return NULL;
#else /* ie. ifndef HAVE_GETPWNAM_R */
/*
* Sorry, there does not appear to be a reentrant version of
* getpwnam(). So, we use the standard libc function.
*/
return getpwnam(user);
#endif /* def HAVE_GETPWNAM_R */
}
/**************** end ****************/
Obiously I hope nobody tries to C&P, I think it would better to look for the functions in the include files and then copy and paste, just because I could made some mistakes coping these lines.After compiling you can copy pam_mkhomedir.so to /usr/lib/security and do not forget to add the following line to the “/etc/pam.conf”
other session required pam_mkhomedir.so skel=/etc/skel umask=0022
Now you can try to log in the system with a ldap’s user:
ssh -l user5 localhost
Password:
Creating directory ‘/export/home/user5′.
Last login: Thu May 14 17:16:21 2009 from localhost
-bash-3.00$
You also can try to access by telnet. The is also backward compability among different versions of Solaris, so that means, it will work out in Solaris 8,9 as well. I hope this information can be useful for somebody.