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.