Entries Tagged 'Solaris' ↓

Sudo + LDAP (II)

The second part of this article is here, so if you missed the first one, you might take a look Part One.

  1. Extending Schema
  2. LDAP support for sudo
  3. LDAP setup
  4. 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.

Compiling sudo in Solaris (Sparc)

I’ve just had a problem trying to compile sudo in Sparc, even I had the right paths to my libraries, I couldn’t get sudo working. Here’s what I did:

I realized that if I took the socket library off, (in bold) the error points me out where the library was (for socket), but not for the intl library. Somehow the path wasn’t set properly.

I thought could be that, so I just told the compiler where to find, instead of looking for in library paths. I directly passed the parameters, -L/usr/local/lib -lint (your path might be different, it depends on where you installed it) and everything worked fine.

gcc -o sudo gram.o alias.o alloc.o defaults.o error.o list.o match.o \
toke.o redblack.o zero_bytes.o sudo_auth.o pam.o ldap.o \
audit.o check.o env.o getspwuid.o gettime.o goodpath.o \
fileops.o find_path.o interfaces.o lbuf.o logging.o parse.o pwutil.o \
set_perms.o sudo.o sudo_edit.o sudo_nss.o term.o tgetpass.o glob.o\
fnmatch.o memrchr.o snprintf.o getprogname.o \
-lintl -lpam -ldl -lsocket -lldap -lnsl
Undefined first referenced
symbol in file
socket interfaces.o (symbol belongs to implicit dependency /usr/lib/libsocket.so.1)
libintl_dgettext pam.o
ld: fatal: Symbol referencing errors. No output written to sudo
collect2: ld returned 1 exit status

Now it works :=)

UPDATE: I found out an option that sets another library path: –with-path=/usr/local/lib.

Forwarded ports and RSA key

When I’m doing port forwarding I always get the same annoying message

tuxman@athome:> ssh -p 3000 someuser@localhost

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that the RSA host key has just been changed.
The fingerprint for the RSA key sent by the remote host is
22:ce:a2:e1:fe:cc:e6:73:cb:03:96:1e:23:3c:5b:55.
Please contact your system administrator.
Add correct host key in /home/tuxman/.ssh/known_hosts to get rid of this message.
Offending key in /home/tuxman/.ssh/known_hosts:159
RSA host key for [localhost]:4000 has changed and you have requested strict checking.
Host key verification failed.

appending some lines to the ~/.ssh/config file, solves the issue

Host localhost
HostKeyAlias youralias

The HostKeyAlias keyword allows us to define an alias and use it instead of the real name. I frequently use ssh port forwarding and I wanted to get rid of this message.

Sudo + LDAP (I)

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 :

  1. Extending Schema
  2. LDAP support for sudo
  3. Structure of LDAP
  4. 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.

PAM modules: pam_mkhomedir for 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;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;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;amp;result);
	if (!status &amp;amp;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;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;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;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.