/* Solaris NSS wrapper for winbind - Shirish Kalele 2000 Based on Luke Howard's ldap_nss module for Solaris */ /* Copyright (C) 1997-2003 Luke Howard. This file is part of the nss_ldap library. The nss_ldap library 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 3 of the License, or (at your option) any later version. The nss_ldap library 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 Library General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with the nss_ldap library; see the file COPYING.LIB. If not, see <http://www.gnu.org/licenses/>. */ #undef DEVELOPER #include "winbind_client.h" #include <stdlib.h> #include <sys/types.h> #include <sys/param.h> #include <string.h> #include <pwd.h> #include "includes.h" #include <syslog.h> #if !defined(HPUX) #include <sys/syslog.h> #endif /*hpux*/ #if defined(HAVE_NSS_COMMON_H) || defined(HPUX) #undef NSS_DEBUG #ifdef NSS_DEBUG #define NSS_DEBUG(str) syslog(LOG_DEBUG, "nss_winbind: %s", str); #else #define NSS_DEBUG(str) ; #endif #define NSS_ARGS(args) ((nss_XbyY_args_t *)args) #ifdef HPUX /* * HP-UX 11 has no definiton of the nss_groupsbymem structure. This * definition is taken from the nss_ldap project at: * http://www.padl.com/OSS/nss_ldap.html */ struct nss_groupsbymem { const char *username; gid_t *gid_array; int maxgids; int force_slow_way; int (*str2ent)(const char *instr, int instr_len, void *ent, char *buffer, int buflen); nss_status_t (*process_cstr)(const char *instr, int instr_len, struct nss_groupsbymem *); int numgids; }; #endif /* HPUX */ #define make_pwent_str(dest, src) \ { \ if((dest = get_static(buffer, buflen, strlen(src)+1)) == NULL) \ { \ *errnop = ERANGE; \ NSS_DEBUG("ERANGE error"); \ return NSS_STATUS_TRYAGAIN; \ } \ strcpy(dest, src); \ } static NSS_STATUS _nss_winbind_setpwent_solwrap (nss_backend_t* be, void* args) { NSS_DEBUG("_nss_winbind_setpwent_solwrap"); return _nss_winbind_setpwent(); } static NSS_STATUS _nss_winbind_endpwent_solwrap (nss_backend_t * be, void *args) { NSS_DEBUG("_nss_winbind_endpwent_solwrap"); return _nss_winbind_endpwent(); } static NSS_STATUS _nss_winbind_getpwent_solwrap (nss_backend_t* be, void *args) { NSS_STATUS ret; char* buffer = NSS_ARGS(args)->buf.buffer; int buflen = NSS_ARGS(args)->buf.buflen; struct passwd* result = (struct passwd*) NSS_ARGS(args)->buf.result; int* errnop = &NSS_ARGS(args)->erange; char logmsg[80]; ret = _nss_winbind_getpwent_r(result, buffer, buflen, errnop); if(ret == NSS_STATUS_SUCCESS) { snprintf(logmsg, 79, "_nss_winbind_getpwent_solwrap: Returning user: %s\n", result->pw_name); NSS_DEBUG(logmsg); NSS_ARGS(args)->returnval = (void*) result; } else { snprintf(logmsg, 79, "_nss_winbind_getpwent_solwrap: Returning error: %d.\n",ret); NSS_DEBUG(logmsg); } return ret; } static NSS_STATUS _nss_winbind_getpwnam_solwrap (nss_backend_t* be, void* args) { NSS_STATUS ret; struct passwd* result = (struct passwd*) NSS_ARGS(args)->buf.result; NSS_DEBUG("_nss_winbind_getpwnam_solwrap"); ret = _nss_winbind_getpwnam_r (NSS_ARGS(args)->key.name, result, NSS_ARGS(args)->buf.buffer, NSS_ARGS(args)->buf.buflen, &NSS_ARGS(args)->erange); if(ret == NSS_STATUS_SUCCESS) NSS_ARGS(args)->returnval = (void*) result; return ret; } static NSS_STATUS _nss_winbind_getpwuid_solwrap(nss_backend_t* be, void* args) { NSS_STATUS ret; struct passwd* result = (struct passwd*) NSS_ARGS(args)->buf.result; NSS_DEBUG("_nss_winbind_getpwuid_solwrap"); ret = _nss_winbind_getpwuid_r (NSS_ARGS(args)->key.uid, result, NSS_ARGS(args)->buf.buffer, NSS_ARGS(args)->buf.buflen, &NSS_ARGS(args)->erange); if(ret == NSS_STATUS_SUCCESS) NSS_ARGS(args)->returnval = (void*) result; return ret; } static NSS_STATUS _nss_winbind_passwd_destr (nss_backend_t * be, void *args) { SAFE_FREE(be); NSS_DEBUG("_nss_winbind_passwd_destr"); return NSS_STATUS_SUCCESS; } static nss_backend_op_t passwd_ops[] = { _nss_winbind_passwd_destr, _nss_winbind_endpwent_solwrap, /* NSS_DBOP_ENDENT */ _nss_winbind_setpwent_solwrap, /* NSS_DBOP_SETENT */ _nss_winbind_getpwent_solwrap, /* NSS_DBOP_GETENT */ _nss_winbind_getpwnam_solwrap, /* NSS_DBOP_PASSWD_BYNAME */ _nss_winbind_getpwuid_solwrap /* NSS_DBOP_PASSWD_BYUID */ }; nss_backend_t* _nss_winbind_passwd_constr (const char* db_name, const char* src_name, const char* cfg_args) { nss_backend_t *be; if(!(be = SMB_MALLOC_P(nss_backend_t)) ) return NULL; be->ops = passwd_ops; be->n_ops = sizeof(passwd_ops) / sizeof(nss_backend_op_t); NSS_DEBUG("Initialized nss_winbind passwd backend"); return be; } /***************************************************************** GROUP database backend *****************************************************************/ static NSS_STATUS _nss_winbind_setgrent_solwrap (nss_backend_t* be, void* args) { NSS_DEBUG("_nss_winbind_setgrent_solwrap"); return _nss_winbind_setgrent(); } static NSS_STATUS _nss_winbind_endgrent_solwrap (nss_backend_t * be, void *args) { NSS_DEBUG("_nss_winbind_endgrent_solwrap"); return _nss_winbind_endgrent(); } static NSS_STATUS _nss_winbind_getgrent_solwrap(nss_backend_t* be, void* args) { NSS_STATUS ret; char* buffer = NSS_ARGS(args)->buf.buffer; int buflen = NSS_ARGS(args)->buf.buflen; struct group* result = (struct group*) NSS_ARGS(args)->buf.result; int* errnop = &NSS_ARGS(args)->erange; char logmsg[80]; ret = _nss_winbind_getgrent_r(result, buffer, buflen, errnop); if(ret == NSS_STATUS_SUCCESS) { snprintf(logmsg, 79, "_nss_winbind_getgrent_solwrap: Returning group: %s\n", result->gr_name); NSS_DEBUG(logmsg); NSS_ARGS(args)->returnval = (void*) result; } else { snprintf(logmsg, 79, "_nss_winbind_getgrent_solwrap: Returning error: %d.\n", ret); NSS_DEBUG(logmsg); } return ret; } static NSS_STATUS _nss_winbind_getgrnam_solwrap(nss_backend_t* be, void* args) { NSS_STATUS ret; struct group* result = (struct group*) NSS_ARGS(args)->buf.result; NSS_DEBUG("_nss_winbind_getgrnam_solwrap"); ret = _nss_winbind_getgrnam_r(NSS_ARGS(args)->key.name, result, NSS_ARGS(args)->buf.buffer, NSS_ARGS(args)->buf.buflen, &NSS_ARGS(args)->erange); if(ret == NSS_STATUS_SUCCESS) NSS_ARGS(args)->returnval = (void*) result; return ret; } static NSS_STATUS _nss_winbind_getgrgid_solwrap(nss_backend_t* be, void* args) { NSS_STATUS ret; struct group* result = (struct group*) NSS_ARGS(args)->buf.result; NSS_DEBUG("_nss_winbind_getgrgid_solwrap"); ret = _nss_winbind_getgrgid_r (NSS_ARGS(args)->key.gid, result, NSS_ARGS(args)->buf.buffer, NSS_ARGS(args)->buf.buflen, &NSS_ARGS(args)->erange); if(ret == NSS_STATUS_SUCCESS) NSS_ARGS(args)->returnval = (void*) result; return ret; } static NSS_STATUS _nss_winbind_getgroupsbymember_solwrap(nss_backend_t* be, void* args) { int errnop; struct nss_groupsbymem *gmem = (struct nss_groupsbymem *)args; long int numgids = gmem->numgids; long int maxgids = gmem->maxgids; NSS_DEBUG("_nss_winbind_getgroupsbymember"); _nss_winbind_initgroups_dyn(gmem->username, gmem->gid_array[0], /* Primary Group */ &numgids, &maxgids, &gmem->gid_array, gmem->maxgids, &errnop); gmem->numgids = numgids; gmem->maxgids = maxgids; /* * If the maximum number of gids have been found, return * SUCCESS so the switch engine will stop searching. Otherwise * return NOTFOUND so nsswitch will continue to get groups * from the remaining database backends specified in the * nsswitch.conf file. */ return (gmem->numgids == gmem->maxgids ? NSS_STATUS_SUCCESS : NSS_STATUS_NOTFOUND); } static NSS_STATUS _nss_winbind_group_destr (nss_backend_t* be, void* args) { SAFE_FREE(be); NSS_DEBUG("_nss_winbind_group_destr"); return NSS_STATUS_SUCCESS; } static nss_backend_op_t group_ops[] = { _nss_winbind_group_destr, _nss_winbind_endgrent_solwrap, _nss_winbind_setgrent_solwrap, _nss_winbind_getgrent_solwrap, _nss_winbind_getgrnam_solwrap, _nss_winbind_getgrgid_solwrap, _nss_winbind_getgroupsbymember_solwrap }; nss_backend_t* _nss_winbind_group_constr (const char* db_name, const char* src_name, const char* cfg_args) { nss_backend_t* be; if(!(be = SMB_MALLOC_P(nss_backend_t)) ) return NULL; be->ops = group_ops; be->n_ops = sizeof(group_ops) / sizeof(nss_backend_op_t); NSS_DEBUG("Initialized nss_winbind group backend"); return be; } /***************************************************************** hosts and ipnodes backend *****************************************************************/ #if defined(SUNOS5) /* not compatible with HP-UX */ /* this parser is shared between get*byname and get*byaddr, as key type in request is stored in different locations, I had to provide the address family as an argument, caller must free the winbind response. */ static NSS_STATUS parse_response(int af, nss_XbyY_args_t* argp, struct winbindd_response *response) { struct hostent *he = (struct hostent *)argp->buf.result; char *buffer = argp->buf.buffer; int buflen = argp->buf.buflen; NSS_STATUS ret; char *p, *data; int addrcount = 0; int len = 0; struct in_addr *addrp; #if defined(AF_INET6) struct in6_addr *addrp6; #endif int i; /* response is tab separated list of ip addresses with hostname and newline at the end. so at first we will strip newline then construct list of addresses for hostent. */ p = strchr(response->data.winsresp, '\n'); if(p) *p = '\0'; else {/* it must be broken */ argp->h_errno = NO_DATA; return NSS_STATUS_UNAVAIL; } for(; p != response->data.winsresp; p--) { if(*p == '\t') addrcount++; } if(addrcount == 0) {/* it must be broken */ argp->h_errno = NO_DATA; return NSS_STATUS_UNAVAIL; } /* allocate space for addresses and h_addr_list */ he->h_addrtype = af; if( he->h_addrtype == AF_INET) { he->h_length = sizeof(struct in_addr); addrp = (struct in_addr *)ROUND_DOWN(buffer + buflen, sizeof(struct in_addr)); addrp -= addrcount; he->h_addr_list = (char **)ROUND_DOWN(addrp, sizeof (char*)); he->h_addr_list -= addrcount+1; } #if defined(AF_INET6) else { he->h_length = sizeof(struct in6_addr); addrp6 = (struct in6_addr *)ROUND_DOWN(buffer + buflen, sizeof(struct in6_addr)); addrp6 -= addrcount; he->h_addr_list = (char **)ROUND_DOWN(addrp6, sizeof (char*)); he->h_addr_list -= addrcount+1; } #endif /* buffer too small?! */ if((char *)he->h_addr_list < buffer ) { argp->erange = 1; return NSS_STR_PARSE_ERANGE; } data = response->data.winsresp; for( i = 0; i < addrcount; i++) { p = strchr(data, '\t'); if(p == NULL) break; /* just in case... */ *p = '\0'; /* terminate the string */ if(he->h_addrtype == AF_INET) { he->h_addr_list[i] = (char *)&addrp[i]; if ((addrp[i].s_addr = inet_addr(data)) == -1) { argp->erange = 1; return NSS_STR_PARSE_ERANGE; } } #if defined(AF_INET6) else { he->h_addr_list[i] = (char *)&addrp6[i]; if (strchr(data, ':') != 0) { if (inet_pton(AF_INET6, data, &addrp6[i]) != 1) { argp->erange = 1; return NSS_STR_PARSE_ERANGE; } } else { struct in_addr in4; if ((in4.s_addr = inet_addr(data)) == -1) { argp->erange = 1; return NSS_STR_PARSE_ERANGE; } IN6_INADDR_TO_V4MAPPED(&in4, &addrp6[i]); } } #endif data = p+1; } he->h_addr_list[i] = (char *)NULL; len = strlen(data); if(len > he->h_addr_list - (char**)argp->buf.buffer) { argp->erange = 1; return NSS_STR_PARSE_ERANGE; } /* this is a bit overkill to use _nss_netdb_aliases here since there seems to be no aliases but it will create all data for us */ he->h_aliases = _nss_netdb_aliases(data, len, buffer, ((char*) he->h_addr_list) - buffer); if(he->h_aliases == NULL) { argp->erange = 1; ret = NSS_STR_PARSE_ERANGE; } else { he->h_name = he->h_aliases[0]; he->h_aliases++; ret = NSS_STR_PARSE_SUCCESS; } argp->returnval = (void*)he; return ret; } static NSS_STATUS _nss_winbind_ipnodes_getbyname(nss_backend_t* be, void *args) { nss_XbyY_args_t *argp = (nss_XbyY_args_t*) args; struct winbindd_response response; struct winbindd_request request; NSS_STATUS ret; int af; ZERO_STRUCT(response); ZERO_STRUCT(request); /* I assume there that AI_ADDRCONFIG cases are handled in nss frontend code, at least it seems done so in solaris... we will give NO_DATA for pure IPv6; IPv4 will be returned for AF_INET or for AF_INET6 and AI_ALL|AI_V4MAPPED we have to map IPv4 to IPv6. */ #if defined(AF_INET6) #ifdef HAVE_NSS_XBYY_KEY_IPNODE af = argp->key.ipnode.af_family; if(af == AF_INET6 && argp->key.ipnode.flags == 0) { argp->h_errno = NO_DATA; return NSS_STATUS_UNAVAIL; } #else /* I'm not that sure if this is correct, but... */ af = AF_INET6; #endif #endif strncpy(request.data.winsreq, argp->key.name, sizeof(request.data.winsreq) - 1); request.data.winsreq[sizeof(request.data.winsreq) - 1] = '\0'; if( (ret = winbindd_request_response(WINBINDD_WINS_BYNAME, &request, &response)) == NSS_STATUS_SUCCESS ) { ret = parse_response(af, argp, &response); } winbindd_free_response(&response); return ret; } static NSS_STATUS _nss_winbind_hosts_getbyname(nss_backend_t* be, void *args) { nss_XbyY_args_t *argp = (nss_XbyY_args_t*) args; struct winbindd_response response; struct winbindd_request request; NSS_STATUS ret; ZERO_STRUCT(response); ZERO_STRUCT(request); strncpy(request.data.winsreq, argp->key.name, sizeof(request.data.winsreq) - 1); request.data.winsreq[sizeof(request.data.winsreq) - 1] = '\0'; if( (ret = winbindd_request_response(WINBINDD_WINS_BYNAME, &request, &response)) == NSS_STATUS_SUCCESS ) { ret = parse_response(AF_INET, argp, &response); } winbindd_free_response(&response); return ret; } static NSS_STATUS _nss_winbind_hosts_getbyaddr(nss_backend_t* be, void *args) { NSS_STATUS ret; struct winbindd_response response; struct winbindd_request request; nss_XbyY_args_t *argp = (nss_XbyY_args_t *)args; const char *p; ZERO_STRUCT(response); ZERO_STRUCT(request); #if defined(AF_INET6) /* winbindd currently does not resolve IPv6 */ if(argp->key.hostaddr.type == AF_INET6) { argp->h_errno = NO_DATA; return NSS_STATUS_UNAVAIL; } p = inet_ntop(argp->key.hostaddr.type, argp->key.hostaddr.addr, request.data.winsreq, sizeof request.data.winsreq); #else snprintf(request.data.winsreq, sizeof request.data.winsreq, "%u.%u.%u.%u", ((unsigned char *)argp->key.hostaddr.addr)[0], ((unsigned char *)argp->key.hostaddr.addr)[1], ((unsigned char *)argp->key.hostaddr.addr)[2], ((unsigned char *)argp->key.hostaddr.addr)[3]); #endif ret = winbindd_request_response(WINBINDD_WINS_BYIP, &request, &response); if( ret == NSS_STATUS_SUCCESS) { parse_response(argp->key.hostaddr.type, argp, &response); } winbindd_free_response(&response); return ret; } /* winbind does not provide setent, getent, endent for wins */ static NSS_STATUS _nss_winbind_common_endent(nss_backend_t* be, void *args) { return (NSS_STATUS_UNAVAIL); } static NSS_STATUS _nss_winbind_common_setent(nss_backend_t* be, void *args) { return (NSS_STATUS_UNAVAIL); } static NSS_STATUS _nss_winbind_common_getent(nss_backend_t* be, void *args) { return (NSS_STATUS_UNAVAIL); } static nss_backend_t* _nss_winbind_common_constr (nss_backend_op_t ops[], int n_ops) { nss_backend_t* be; if(!(be = SMB_MALLOC_P(nss_backend_t)) ) return NULL; be->ops = ops; be->n_ops = n_ops; return be; } static NSS_STATUS _nss_winbind_common_destr (nss_backend_t* be, void* args) { SAFE_FREE(be); return NSS_STATUS_SUCCESS; } static nss_backend_op_t ipnodes_ops[] = { _nss_winbind_common_destr, _nss_winbind_common_endent, _nss_winbind_common_setent, _nss_winbind_common_getent, _nss_winbind_ipnodes_getbyname, _nss_winbind_hosts_getbyaddr, }; nss_backend_t * _nss_winbind_ipnodes_constr(dummy1, dummy2, dummy3) const char *dummy1, *dummy2, *dummy3; { return (_nss_winbind_common_constr(ipnodes_ops, sizeof (ipnodes_ops) / sizeof (ipnodes_ops[0]))); } static nss_backend_op_t host_ops[] = { _nss_winbind_common_destr, _nss_winbind_common_endent, _nss_winbind_common_setent, _nss_winbind_common_getent, _nss_winbind_hosts_getbyname, _nss_winbind_hosts_getbyaddr, }; nss_backend_t * _nss_winbind_hosts_constr(dummy1, dummy2, dummy3) const char *dummy1, *dummy2, *dummy3; { return (_nss_winbind_common_constr(host_ops, sizeof (host_ops) / sizeof (host_ops[0]))); } #endif /* defined(SUNOS5) */ #endif /* defined(HAVE_NSS_COMMON_H) || defined(HPUX) */