/* Copyright 2005 Laurent Wacrenier This file is part of libhome libhome is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. libhome 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with libhome; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #define SOCKET_PATH "/var/run/home_proxy" static char const rcsid[] UNUSED = "$Id: nss_home_proxy.c,v 1.11 2005/07/04 11:09:28 lwa Exp $"; #include #include #include #include #include #include #include #if HAVE_STDINT_H #include #endif #include #include #include #include #if defined(HAVE_NSS_COMMON_H) /* Solaris */ /* #warning solaris */ #include #include typedef nss_status_t NSS_STATUS; #define NSS_STATUS_SUCCESS NSS_SUCCESS #define NSS_STATUS_NOTFOUND NSS_NOTFOUND #define NSS_STATUS_UNAVAIL NSS_UNAVAIL #define NSS_STATUS_TRYAGAIN NSS_TRYAGAIN #define NSS_SOLARIS #elif !defined(HPUX) && defined(HAVE_NSSWITCH_H) && defined(HAVE_NSS_H) /* FreeBSD */ /* #warning freebsd */ #define NSS_FREEBSD #include typedef int NSS_STATUS; #define NSS_STATUS_SUCCESS NS_SUCCESS #define NSS_STATUS_NOTFOUND NS_NOTFOUND #define NSS_STATUS_UNAVAIL NS_UNAVAIL #define NSS_STATUS_TRYAGAIN NS_TRYAGAIN #elif defined(HAVE_NSS_H) /* Linux */ /* #warning linux */ #define NSS_LINUX #include typedef enum nss_status NSS_STATUS; #elif defined(HPUX) && defined (HAVE_NSSWITCH_H) #warning HP-UX /* HPUX 11 */ #include /* HP-UX lacks of needed headers */ /* If you want to port, you have to toke Solaris one's */ /* and guess how it works (probably like Solaris API) */ typedef enum { NSS_SUCCESS, NSS_NOTFOUND, NSS_UNAVAIL, NSS_TRYAGAIN } nss_status_t; typedef nss_status_t NSS_STATUS; #define NSS_STATUS_SUCCESS NSS_SUCCESS #define NSS_STATUS_NOTFOUND NSS_NOTFOUND #define NSS_STATUS_UNAVAIL NSS_UNAVAIL #define NSS_STATUS_TRYAGAIN NSS_TRYAGAIN #else #error unknown #define NSS_UNAVAIABLE #endif #ifndef NSS_UNAVAIABLE struct context { struct passwd *pwd; char *buffer; size_t bufsize; int *errnop; }; static NSS_STATUS home_proxy_query(const char *query, ssize_t query_len, struct passwd *pwd, char *buffer, size_t bufsize, int *errnop) { int s; uint32_t len; struct sockaddr_un sa_un; ssize_t readlen; s = socket(AF_UNIX, SOCK_STREAM, 0); if (s == -1) { syslog(LOG_WARNING, "nss_home_proxy: socket(): %s", strerror(errno)); *errnop = errno; return NSS_STATUS_UNAVAIL; } memset(&sa_un, 0, sizeof(struct sockaddr_un)); sa_un.sun_family = AF_UNIX; sa_un.sun_path[0] = 0; strncat(sa_un.sun_path, SOCKET_PATH, sizeof(sa_un.sun_path)-1); if (connect(s, (struct sockaddr *)&sa_un, sizeof(sa_un)) == -1) { *errnop = errno; close(s); syslog(LOG_WARNING, "nss_home_proxy: unable to connect to socket %s: %s", SOCKET_PATH, strerror(errno)); return NSS_STATUS_UNAVAIL; } if (write(s, query, query_len+1) == 0) { *errnop = errno; close(s); syslog(LOG_WARNING, "nss_home_proxy: unable to write to socket %s: %s", SOCKET_PATH, strerror(errno)); return NSS_STATUS_UNAVAIL; } shutdown(s, SHUT_WR); readlen = read(s, &len, sizeof(len)); if (readlen != sizeof(len)) { syslog(LOG_WARNING, "nss_home_proxy: proxy read length mismatch (%lu != %lu)", (unsigned long)sizeof(len), (unsigned long)readlen); close(s); return NSS_STATUS_UNAVAIL; } if (len == 0) { close(s); return NSS_STATUS_NOTFOUND; } if (readlen > bufsize) { *errnop = ERANGE; return NSS_STATUS_NOTFOUND; } readlen = read(s, buffer, bufsize); close(s); memset(pwd, 0, sizeof(struct passwd)); pwd->pw_name = buffer; buffer = strchr(buffer, 0)+1; pwd->pw_passwd = buffer; buffer = strchr(buffer, 0)+1; pwd->pw_uid=*buffer ? (uid_t)strtoul(buffer, NULL, 10) : (uid_t)-1; buffer = strchr(buffer, 0)+1; pwd->pw_gid=*buffer ? (gid_t)strtoul(buffer, NULL, 10) : (gid_t)-1; buffer = strchr(buffer, 0)+1; #ifdef HAVE_STRUCT_PASSWD_PW_CLASS pwd->pw_class = buffer; #endif buffer = strchr(buffer, 0)+1; pwd->pw_gecos = buffer; buffer = strchr(buffer, 0)+1; pwd->pw_dir = buffer; buffer = strchr(buffer, 0)+1; pwd->pw_shell = buffer; buffer = strchr(buffer, 0)+1; #ifdef HAVE_STRUCT_PASSWD_PW_CHANGE pwd->pw_change=0; #endif #ifdef HAVE_STRUCT_PASSWD_PW_EXPIRE pwd->pw_expire=*buffer ? (gid_t)strtoul(buffer, NULL, 10) : 0; #endif buffer = strchr(buffer, 0)+1; #ifdef HAVE_STRUCT_PASSWD_PW_QUOTA pwd->pw_quota= *buffer ? (int)strtol(buffer, NULL, 10) : 0; #endif return NSS_STATUS_SUCCESS; } static NSS_STATUS query_name(const char *name, struct passwd *pwd, char *buffer, size_t bufsize, int *errnop) { if (*name == '\xff') return NSS_STATUS_NOTFOUND; return home_proxy_query(name, strlen(name), pwd, buffer, bufsize, errnop); } static NSS_STATUS query_uid(uid_t uid, struct passwd *pwd, char *buffer, size_t bufsize, int *errnop) { char query[1000]; int query_len = snprintf(query, sizeof(query)-1, "\xff%lu", (unsigned long)uid); query[sizeof(query)-1] = 0; return home_proxy_query(query, query_len, pwd, buffer, bufsize, errnop); } #if defined(NSS_FREEBSD) /* HOWTO: http://uw714doc.sco.com/en/SEC_admin/nssover.html */ static NSS_STATUS home_proxy_unavail(void *retval, void *mdata, va_list ap) { return NSS_STATUS_UNAVAIL; } static NSS_STATUS get_uid(void *retval, void *mdata, va_list ap) { NSS_STATUS ret; uid_t uid = va_arg(ap, uid_t); struct passwd *pwd = va_arg(ap, struct passwd *); char *buffer = va_arg(ap, char *); size_t bufsize = va_arg(ap, size_t); int *errnop = va_arg(ap, int *); ret = query_uid(uid, pwd, buffer, bufsize, errnop); if (retval && ret == NSS_STATUS_SUCCESS) *(struct passwd **)retval = pwd; return ret; } static NSS_STATUS get_name(void *retval, void *mdata, va_list ap) { NSS_STATUS ret; const char *name = va_arg(ap, const char *); struct passwd *pwd = va_arg(ap, struct passwd *); char *buffer = va_arg(ap, char *); size_t bufsize = va_arg(ap, size_t); int *errnop = va_arg(ap, int *); ret = query_name(name, pwd, buffer, bufsize, errnop); if (retval && ret == NSS_STATUS_SUCCESS) *(struct passwd **)retval = pwd; return ret; } ns_mtab *nss_module_register(const char *modname, unsigned int *plen, nss_module_unregister_fn *fptr) { static ns_mtab mtab[] = { {"passwd", "getpwuid_r", &get_uid, NULL }, {"passwd", "getpwnam_r", &get_name, NULL }, {"passwd", "endpwent", &home_proxy_unavail, NULL}, {"passwd", "setpwent", &home_proxy_unavail, NULL}, }; *plen = 2; /* one ns_mtab item */ *fptr = 0; /* no cleanup needed */ return mtab; } #elif defined(NSS_LINUX) /* Linux: http://www.delorie.com/gnu/docs/glibc/libc_602.html */ NSS_STATUS _nss_home_proxy_getpwnam_r(char *name, struct passwd *result, char *buffer, size_t buflen, int *errnop) { return query_name(name, result, buffer, buflen, errnop); } NSS_STATUS _nss_home_proxy_getpwuid_r(uid_t uid, struct passwd *result, char *buffer, size_t buflen, int *errnop) { return query_uid(uid, result, buffer, buflen, errnop); } NSS_STATUS _nss_home_proxy_setpwent(void) { return NSS_STATUS_UNAVAIL; } NSS_STATUS _nss_home_proxy_endpwent(void) { return NSS_STATUS_UNAVAIL; } #elif defined (NSS_SOLARIS) static NSS_STATUS nss_passwd_destr(nss_backend_t *be, void *args) { free(be); return NSS_STATUS_SUCCESS; } static NSS_STATUS nss_unavail(nss_backend_t *be, void *args) { return NSS_STATUS_UNAVAIL; } static NSS_STATUS nss_getpwnam_r(nss_backend_t *be, void *args) { nss_XbyY_args_t *a = args; static NSS_STATUS ret; const char *name = a->key.name; ret = query_name(name, a->buf.result, a->buf.buffer, a->buf.buflen, &a->erange); if (ret == NSS_STATUS_SUCCESS) a->returnval = a->buf.result; return ret; } static NSS_STATUS nss_getpwuid_r(nss_backend_t *be, void *args) { nss_XbyY_args_t *a = args; static NSS_STATUS ret; uid_t uid = a->key.uid; ret = query_uid(uid, a->buf.result, a->buf.buffer, a->buf.buflen, &a->erange); if (ret == NSS_STATUS_SUCCESS) a->returnval = a->buf.result; return ret; } static nss_backend_op_t passwd_ops[] = { nss_passwd_destr, nss_unavail, /* NSS_DBOP_ENDENT */ nss_unavail, /* NSS_DBOP_SETENT */ nss_unavail, /* NSS_DBOP_GETENT */ nss_getpwnam_r, /* NSS_DBOP_PASSWD_BYNAME */ nss_getpwuid_r /* NSS_DBOP_PASSWD_BYUID */ }; nss_backend_t * _nss_home_proxy_passwd_constr(const char *db_name, const char *src_name, const char *cfg_args) { nss_backend_t *be; be = (nss_backend_t *) malloc (sizeof (*be)); if (!be) return NULL; be->ops = passwd_ops; be->n_ops = sizeof (passwd_ops) / sizeof (nss_backend_op_t); return be; } #endif #endif /* NSS_UNAVAIABLE */