| /* Copyright (c) 1997,1999,2001,2003,2005,2006 Free Software Foundation, Inc. |
| This file is part of the GNU C Library. |
| Contributed by Thorsten Kukuk <kukuk@suse.de>, 1997. |
| |
| The GNU C 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 2.1 of the License, or (at your option) any later version. |
| |
| The GNU C 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 |
| Lesser General Public License for more details. |
| |
| You should have received a copy of the GNU Lesser General Public |
| License along with the GNU C Library; if not, write to the Free |
| Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA |
| 02111-1307 USA. */ |
| |
| #include <nss.h> |
| #include <ctype.h> |
| #include <errno.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <libintl.h> |
| #include <syslog.h> |
| #include <rpc/rpc.h> |
| #include <rpcsvc/nis.h> |
| #include <rpc/key_prot.h> |
| extern int xdecrypt (char *, char *); |
| |
| #include <nss-nisplus.h> |
| |
| /* If we haven't found the entry, we give a SUCCESS and an empty key back. */ |
| enum nss_status |
| _nss_nisplus_getpublickey (const char *netname, char *pkey, int *errnop) |
| { |
| nis_result *res; |
| enum nss_status retval; |
| char buf[NIS_MAXNAMELEN + 2]; |
| size_t slen; |
| char *domain, *cptr; |
| int len; |
| |
| pkey[0] = 0; |
| |
| if (netname == NULL) |
| { |
| *errnop = EINVAL; |
| return NSS_STATUS_UNAVAIL; |
| } |
| |
| domain = strchr (netname, '@'); |
| if (!domain) |
| return NSS_STATUS_UNAVAIL; |
| domain++; |
| |
| slen = snprintf (buf, NIS_MAXNAMELEN, |
| "[auth_name=%s,auth_type=DES],cred.org_dir.%s", |
| netname, domain); |
| |
| if (slen >= NIS_MAXNAMELEN) |
| { |
| *errnop = EINVAL; |
| return NSS_STATUS_UNAVAIL; |
| } |
| |
| if (buf[slen - 1] != '.') |
| { |
| buf[slen++] = '.'; |
| buf[slen] = '\0'; |
| } |
| |
| res = nis_list (buf, USE_DGRAM+NO_AUTHINFO+FOLLOW_LINKS+FOLLOW_PATH, |
| NULL, NULL); |
| |
| if (res == NULL) |
| { |
| *errnop = ENOMEM; |
| return NSS_STATUS_TRYAGAIN; |
| } |
| retval = niserr2nss (res->status); |
| |
| if (retval != NSS_STATUS_SUCCESS) |
| { |
| if (retval == NSS_STATUS_TRYAGAIN) |
| *errnop = errno; |
| if (res->status == NIS_NOTFOUND) |
| retval = NSS_STATUS_SUCCESS; |
| nis_freeresult (res); |
| return retval; |
| } |
| |
| if (NIS_RES_NUMOBJ (res) > 1) |
| { |
| /* |
| * More than one principal with same uid? |
| * something wrong with cred table. Should be unique |
| * Warn user and continue. |
| */ |
| syslog (LOG_ERR, _("DES entry for netname %s not unique\n"), netname); |
| nis_freeresult (res); |
| return NSS_STATUS_SUCCESS; |
| } |
| |
| len = ENTRY_LEN (NIS_RES_OBJECT (res), 3); |
| memcpy (pkey, ENTRY_VAL (NIS_RES_OBJECT (res),3), len); |
| pkey[len] = 0; |
| cptr = strchr (pkey, ':'); |
| if (cptr) |
| cptr[0] = '\0'; |
| nis_freeresult (res); |
| |
| return NSS_STATUS_SUCCESS; |
| } |
| |
| |
| enum nss_status |
| _nss_nisplus_getsecretkey (const char *netname, char *skey, char *passwd, |
| int *errnop) |
| { |
| nis_result *res; |
| enum nss_status retval; |
| char buf[NIS_MAXNAMELEN + 2]; |
| size_t slen; |
| char *domain, *cptr; |
| int len; |
| |
| skey[0] = 0; |
| |
| if (netname == NULL) |
| { |
| *errnop = EINVAL; |
| return NSS_STATUS_UNAVAIL; |
| } |
| |
| domain = strchr (netname, '@'); |
| if (!domain) |
| return NSS_STATUS_UNAVAIL; |
| domain++; |
| |
| slen = snprintf (buf, NIS_MAXNAMELEN, |
| "[auth_name=%s,auth_type=DES],cred.org_dir.%s", |
| netname, domain); |
| |
| if (slen >= NIS_MAXNAMELEN) |
| { |
| *errnop = EINVAL; |
| return NSS_STATUS_UNAVAIL; |
| } |
| |
| if (buf[slen - 1] != '.') |
| { |
| buf[slen++] = '.'; |
| buf[slen] = '\0'; |
| } |
| |
| res = nis_list (buf, USE_DGRAM | NO_AUTHINFO | FOLLOW_LINKS | FOLLOW_PATH, |
| NULL, NULL); |
| |
| if (res == NULL) |
| { |
| *errnop = ENOMEM; |
| return NSS_STATUS_TRYAGAIN; |
| } |
| retval = niserr2nss (res->status); |
| |
| if (retval != NSS_STATUS_SUCCESS) |
| { |
| if (retval == NSS_STATUS_TRYAGAIN) |
| *errnop = errno; |
| nis_freeresult (res); |
| return retval; |
| } |
| |
| if (NIS_RES_NUMOBJ (res) > 1) |
| { |
| /* |
| * More than one principal with same uid? |
| * something wrong with cred table. Should be unique |
| * Warn user and continue. |
| */ |
| syslog (LOG_ERR, _("DES entry for netname %s not unique\n"), netname); |
| nis_freeresult (res); |
| return NSS_STATUS_SUCCESS; |
| } |
| |
| len = ENTRY_LEN (NIS_RES_OBJECT (res), 4); |
| memcpy (buf, ENTRY_VAL (NIS_RES_OBJECT (res), 4), len); |
| buf[len] = '\0'; |
| cptr = strchr (buf, ':'); |
| if (cptr) |
| cptr[0] = '\0'; |
| nis_freeresult (res); |
| |
| if (!xdecrypt (buf, passwd)) |
| return NSS_STATUS_SUCCESS; |
| |
| if (memcmp (buf, &(buf[HEXKEYBYTES]), KEYCHECKSUMSIZE) != 0) |
| return NSS_STATUS_SUCCESS; |
| |
| buf[HEXKEYBYTES] = 0; |
| strcpy (skey, buf); |
| |
| return NSS_STATUS_SUCCESS; |
| } |
| |
| |
| /* Parse information from the passed string. |
| The format of the string passed is gid,grp,grp, ... */ |
| static enum nss_status |
| parse_grp_str (const char *s, gid_t *gidp, int *gidlenp, gid_t *gidlist, |
| int *errnop) |
| { |
| char *ep; |
| int gidlen; |
| |
| if (!s || (!isdigit (*s))) |
| { |
| syslog (LOG_ERR, _("netname2user: missing group id list in `%s'"), s); |
| return NSS_STATUS_NOTFOUND; |
| } |
| |
| *gidp = strtoul (s, &ep, 10); |
| |
| gidlen = 0; |
| |
| /* After strtoul() ep should point to the marker ',', which means |
| here starts a new value. |
| |
| The Sun man pages show that GIDLIST should contain at least NGRPS |
| elements. Limiting the number written by this value is the best |
| we can do. */ |
| while (ep != NULL && *ep == ',' && gidlen < NGRPS) |
| { |
| ep++; |
| s = ep; |
| gidlist[gidlen++] = strtoul (s, &ep, 10); |
| } |
| *gidlenp = gidlen; |
| |
| return NSS_STATUS_SUCCESS; |
| } |
| |
| enum nss_status |
| _nss_nisplus_netname2user (char netname[MAXNETNAMELEN + 1], uid_t *uidp, |
| gid_t *gidp, int *gidlenp, gid_t *gidlist, int *errnop) |
| { |
| char *domain; |
| nis_result *res; |
| char sname[NIS_MAXNAMELEN + 2]; /* search criteria + table name */ |
| size_t slen; |
| char principal[NIS_MAXNAMELEN + 1]; |
| int len; |
| |
| /* 1. Get home domain of user. */ |
| domain = strchr (netname, '@'); |
| if (! domain) |
| return NSS_STATUS_UNAVAIL; |
| |
| ++domain; /* skip '@' */ |
| |
| /* 2. Get user's nisplus principal name. */ |
| slen = snprintf (sname, NIS_MAXNAMELEN, |
| "[auth_name=%s,auth_type=DES],cred.org_dir.%s", |
| netname, domain); |
| |
| if (slen >= NIS_MAXNAMELEN) |
| { |
| *errnop = EINVAL; |
| return NSS_STATUS_UNAVAIL; |
| } |
| |
| if (sname[slen - 1] != '.') |
| { |
| sname[slen++] = '.'; |
| sname[slen] = '\0'; |
| } |
| |
| /* must use authenticated call here */ |
| /* XXX but we cant, for now. XXX */ |
| res = nis_list (sname, USE_DGRAM+NO_AUTHINFO+FOLLOW_LINKS+FOLLOW_PATH, |
| NULL, NULL); |
| if (res == NULL) |
| { |
| *errnop = ENOMEM; |
| return NSS_STATUS_TRYAGAIN; |
| } |
| switch (res->status) |
| { |
| case NIS_SUCCESS: |
| case NIS_S_SUCCESS: |
| break; /* go and do something useful */ |
| case NIS_NOTFOUND: |
| case NIS_PARTIAL: |
| case NIS_NOSUCHNAME: |
| case NIS_NOSUCHTABLE: |
| nis_freeresult (res); |
| return NSS_STATUS_NOTFOUND; |
| case NIS_S_NOTFOUND: |
| case NIS_TRYAGAIN: |
| syslog (LOG_ERR, _("netname2user: (nis+ lookup): %s\n"), |
| nis_sperrno (res->status)); |
| nis_freeresult (res); |
| *errnop = errno; |
| return NSS_STATUS_TRYAGAIN; |
| default: |
| syslog (LOG_ERR, _("netname2user: (nis+ lookup): %s\n"), |
| nis_sperrno (res->status)); |
| nis_freeresult (res); |
| return NSS_STATUS_UNAVAIL; |
| } |
| |
| if (NIS_RES_NUMOBJ (res) > 1) |
| /* |
| * A netname belonging to more than one principal? |
| * Something wrong with cred table. should be unique. |
| * Warn user and continue. |
| */ |
| syslog (LOG_ALERT, |
| _("netname2user: DES entry for %s in directory %s not unique"), |
| netname, domain); |
| |
| len = ENTRY_LEN (NIS_RES_OBJECT (res), 0); |
| strncpy (principal, ENTRY_VAL (NIS_RES_OBJECT (res), 0), len); |
| principal[len] = '\0'; |
| nis_freeresult (res); |
| |
| if (principal[0] == '\0') |
| return NSS_STATUS_UNAVAIL; |
| |
| /* |
| * 3. Use principal name to look up uid/gid information in |
| * LOCAL entry in **local** cred table. |
| */ |
| domain = nis_local_directory (); |
| if (strlen (principal) + strlen (domain) + 45 > (size_t) NIS_MAXNAMELEN) |
| { |
| syslog (LOG_ERR, _("netname2user: principal name `%s' too long"), |
| principal); |
| return NSS_STATUS_UNAVAIL; |
| } |
| |
| slen = snprintf (sname, sizeof (sname), |
| "[cname=%s,auth_type=LOCAL],cred.org_dir.%s", |
| principal, domain); |
| |
| if (sname[slen - 1] != '.') |
| { |
| sname[slen++] = '.'; |
| sname[slen] = '\0'; |
| } |
| |
| /* must use authenticated call here */ |
| /* XXX but we cant, for now. XXX */ |
| res = nis_list (sname, USE_DGRAM+NO_AUTHINFO+FOLLOW_LINKS+FOLLOW_PATH, |
| NULL, NULL); |
| if (res == NULL) |
| { |
| *errnop = ENOMEM; |
| return NSS_STATUS_TRYAGAIN; |
| } |
| switch(res->status) |
| { |
| case NIS_NOTFOUND: |
| case NIS_PARTIAL: |
| case NIS_NOSUCHNAME: |
| case NIS_NOSUCHTABLE: |
| nis_freeresult (res); |
| return NSS_STATUS_NOTFOUND; |
| case NIS_S_NOTFOUND: |
| case NIS_TRYAGAIN: |
| syslog (LOG_ERR, _("netname2user: (nis+ lookup): %s\n"), |
| nis_sperrno (res->status)); |
| nis_freeresult (res); |
| *errnop = errno; |
| return NSS_STATUS_TRYAGAIN; |
| case NIS_SUCCESS: |
| case NIS_S_SUCCESS: |
| break; /* go and do something useful */ |
| default: |
| syslog (LOG_ERR, _("netname2user: (nis+ lookup): %s\n"), |
| nis_sperrno (res->status)); |
| nis_freeresult (res); |
| return NSS_STATUS_UNAVAIL; |
| } |
| |
| if (NIS_RES_NUMOBJ (res) > 1) |
| /* |
| * A principal can have more than one LOCAL entry? |
| * Something wrong with cred table. |
| * Warn user and continue. |
| */ |
| syslog (LOG_ALERT, |
| _("netname2user: LOCAL entry for %s in directory %s not unique"), |
| netname, domain); |
| /* Fetch the uid */ |
| *uidp = strtoul (ENTRY_VAL (NIS_RES_OBJECT (res), 2), NULL, 10); |
| |
| if (*uidp == 0) |
| { |
| syslog (LOG_ERR, _("netname2user: should not have uid 0")); |
| nis_freeresult (res); |
| return NSS_STATUS_NOTFOUND; |
| } |
| |
| parse_grp_str (ENTRY_VAL (NIS_RES_OBJECT (res), 3), |
| gidp, gidlenp, gidlist, errnop); |
| |
| nis_freeresult (res); |
| return NSS_STATUS_SUCCESS; |
| } |