| 1 | /* Copyright (c) 1997-2016 Free Software Foundation, Inc. |
| 2 | This file is part of the GNU C Library. |
| 3 | Contributed by Thorsten Kukuk <kukuk@suse.de>, 1997. |
| 4 | |
| 5 | The GNU C Library is free software; you can redistribute it and/or |
| 6 | modify it under the terms of the GNU Lesser General Public |
| 7 | License as published by the Free Software Foundation; either |
| 8 | version 2.1 of the License, or (at your option) any later version. |
| 9 | |
| 10 | The GNU C Library is distributed in the hope that it will be useful, |
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 13 | Lesser General Public License for more details. |
| 14 | |
| 15 | You should have received a copy of the GNU Lesser General Public |
| 16 | License along with the GNU C Library; if not, see |
| 17 | <http://www.gnu.org/licenses/>. */ |
| 18 | |
| 19 | #include <nss.h> |
| 20 | #include <ctype.h> |
| 21 | #include <errno.h> |
| 22 | #include <stdio.h> |
| 23 | #include <string.h> |
| 24 | #include <libintl.h> |
| 25 | #include <syslog.h> |
| 26 | #include <rpc/rpc.h> |
| 27 | #include <rpcsvc/nis.h> |
| 28 | #include <rpc/key_prot.h> |
| 29 | extern int xdecrypt (char *, char *); |
| 30 | |
| 31 | #include <nss-nisplus.h> |
| 32 | |
| 33 | /* If we haven't found the entry, we give a SUCCESS and an empty key back. */ |
| 34 | enum nss_status |
| 35 | _nss_nisplus_getpublickey (const char *netname, char *pkey, int *errnop) |
| 36 | { |
| 37 | nis_result *res; |
| 38 | enum nss_status retval; |
| 39 | char buf[NIS_MAXNAMELEN + 2]; |
| 40 | size_t slen; |
| 41 | char *domain, *cptr; |
| 42 | int len; |
| 43 | |
| 44 | pkey[0] = 0; |
| 45 | |
| 46 | if (netname == NULL) |
| 47 | { |
| 48 | *errnop = EINVAL; |
| 49 | return NSS_STATUS_UNAVAIL; |
| 50 | } |
| 51 | |
| 52 | domain = strchr (netname, '@'); |
| 53 | if (!domain) |
| 54 | return NSS_STATUS_UNAVAIL; |
| 55 | domain++; |
| 56 | |
| 57 | slen = snprintf (buf, NIS_MAXNAMELEN, |
| 58 | "[auth_name=%s,auth_type=DES],cred.org_dir.%s" , |
| 59 | netname, domain); |
| 60 | |
| 61 | if (slen >= NIS_MAXNAMELEN) |
| 62 | { |
| 63 | *errnop = EINVAL; |
| 64 | return NSS_STATUS_UNAVAIL; |
| 65 | } |
| 66 | |
| 67 | if (buf[slen - 1] != '.') |
| 68 | { |
| 69 | buf[slen++] = '.'; |
| 70 | buf[slen] = '\0'; |
| 71 | } |
| 72 | |
| 73 | res = nis_list (buf, USE_DGRAM+NO_AUTHINFO+FOLLOW_LINKS+FOLLOW_PATH, |
| 74 | NULL, NULL); |
| 75 | |
| 76 | if (res == NULL) |
| 77 | { |
| 78 | *errnop = ENOMEM; |
| 79 | return NSS_STATUS_TRYAGAIN; |
| 80 | } |
| 81 | retval = niserr2nss (res->status); |
| 82 | |
| 83 | if (retval != NSS_STATUS_SUCCESS) |
| 84 | { |
| 85 | if (retval == NSS_STATUS_TRYAGAIN) |
| 86 | *errnop = errno; |
| 87 | if (res->status == NIS_NOTFOUND) |
| 88 | retval = NSS_STATUS_SUCCESS; |
| 89 | nis_freeresult (res); |
| 90 | return retval; |
| 91 | } |
| 92 | |
| 93 | if (NIS_RES_NUMOBJ (res) > 1) |
| 94 | { |
| 95 | /* |
| 96 | * More than one principal with same uid? |
| 97 | * something wrong with cred table. Should be unique |
| 98 | * Warn user and continue. |
| 99 | */ |
| 100 | syslog (LOG_ERR, _("DES entry for netname %s not unique\n" ), netname); |
| 101 | nis_freeresult (res); |
| 102 | return NSS_STATUS_SUCCESS; |
| 103 | } |
| 104 | |
| 105 | len = ENTRY_LEN (NIS_RES_OBJECT (res), 3); |
| 106 | memcpy (pkey, ENTRY_VAL (NIS_RES_OBJECT (res),3), len); |
| 107 | pkey[len] = 0; |
| 108 | cptr = strchr (pkey, ':'); |
| 109 | if (cptr) |
| 110 | cptr[0] = '\0'; |
| 111 | nis_freeresult (res); |
| 112 | |
| 113 | return NSS_STATUS_SUCCESS; |
| 114 | } |
| 115 | |
| 116 | |
| 117 | enum nss_status |
| 118 | _nss_nisplus_getsecretkey (const char *netname, char *skey, char *passwd, |
| 119 | int *errnop) |
| 120 | { |
| 121 | nis_result *res; |
| 122 | enum nss_status retval; |
| 123 | char buf[NIS_MAXNAMELEN + 2]; |
| 124 | size_t slen; |
| 125 | char *domain, *cptr; |
| 126 | int len; |
| 127 | |
| 128 | skey[0] = 0; |
| 129 | |
| 130 | if (netname == NULL) |
| 131 | { |
| 132 | *errnop = EINVAL; |
| 133 | return NSS_STATUS_UNAVAIL; |
| 134 | } |
| 135 | |
| 136 | domain = strchr (netname, '@'); |
| 137 | if (!domain) |
| 138 | return NSS_STATUS_UNAVAIL; |
| 139 | domain++; |
| 140 | |
| 141 | slen = snprintf (buf, NIS_MAXNAMELEN, |
| 142 | "[auth_name=%s,auth_type=DES],cred.org_dir.%s" , |
| 143 | netname, domain); |
| 144 | |
| 145 | if (slen >= NIS_MAXNAMELEN) |
| 146 | { |
| 147 | *errnop = EINVAL; |
| 148 | return NSS_STATUS_UNAVAIL; |
| 149 | } |
| 150 | |
| 151 | if (buf[slen - 1] != '.') |
| 152 | { |
| 153 | buf[slen++] = '.'; |
| 154 | buf[slen] = '\0'; |
| 155 | } |
| 156 | |
| 157 | res = nis_list (buf, USE_DGRAM | NO_AUTHINFO | FOLLOW_LINKS | FOLLOW_PATH, |
| 158 | NULL, NULL); |
| 159 | |
| 160 | if (res == NULL) |
| 161 | { |
| 162 | *errnop = ENOMEM; |
| 163 | return NSS_STATUS_TRYAGAIN; |
| 164 | } |
| 165 | retval = niserr2nss (res->status); |
| 166 | |
| 167 | if (retval != NSS_STATUS_SUCCESS) |
| 168 | { |
| 169 | if (retval == NSS_STATUS_TRYAGAIN) |
| 170 | *errnop = errno; |
| 171 | nis_freeresult (res); |
| 172 | return retval; |
| 173 | } |
| 174 | |
| 175 | if (NIS_RES_NUMOBJ (res) > 1) |
| 176 | { |
| 177 | /* |
| 178 | * More than one principal with same uid? |
| 179 | * something wrong with cred table. Should be unique |
| 180 | * Warn user and continue. |
| 181 | */ |
| 182 | syslog (LOG_ERR, _("DES entry for netname %s not unique\n" ), netname); |
| 183 | nis_freeresult (res); |
| 184 | return NSS_STATUS_SUCCESS; |
| 185 | } |
| 186 | |
| 187 | len = ENTRY_LEN (NIS_RES_OBJECT (res), 4); |
| 188 | memcpy (buf, ENTRY_VAL (NIS_RES_OBJECT (res), 4), len); |
| 189 | buf[len] = '\0'; |
| 190 | cptr = strchr (buf, ':'); |
| 191 | if (cptr) |
| 192 | cptr[0] = '\0'; |
| 193 | nis_freeresult (res); |
| 194 | |
| 195 | if (!xdecrypt (buf, passwd)) |
| 196 | return NSS_STATUS_SUCCESS; |
| 197 | |
| 198 | if (memcmp (buf, &(buf[HEXKEYBYTES]), KEYCHECKSUMSIZE) != 0) |
| 199 | return NSS_STATUS_SUCCESS; |
| 200 | |
| 201 | buf[HEXKEYBYTES] = 0; |
| 202 | strcpy (skey, buf); |
| 203 | |
| 204 | return NSS_STATUS_SUCCESS; |
| 205 | } |
| 206 | |
| 207 | |
| 208 | /* Parse information from the passed string. |
| 209 | The format of the string passed is gid,grp,grp, ... */ |
| 210 | static enum nss_status |
| 211 | parse_grp_str (const char *s, gid_t *gidp, int *gidlenp, gid_t *gidlist, |
| 212 | int *errnop) |
| 213 | { |
| 214 | char *ep; |
| 215 | int gidlen; |
| 216 | |
| 217 | if (!s || (!isdigit (*s))) |
| 218 | { |
| 219 | syslog (LOG_ERR, _("netname2user: missing group id list in `%s'" ), s); |
| 220 | return NSS_STATUS_NOTFOUND; |
| 221 | } |
| 222 | |
| 223 | *gidp = strtoul (s, &ep, 10); |
| 224 | |
| 225 | gidlen = 0; |
| 226 | |
| 227 | /* After strtoul() ep should point to the marker ',', which means |
| 228 | here starts a new value. |
| 229 | |
| 230 | The Sun man pages show that GIDLIST should contain at least NGRPS |
| 231 | elements. Limiting the number written by this value is the best |
| 232 | we can do. */ |
| 233 | while (ep != NULL && *ep == ',' && gidlen < NGRPS) |
| 234 | { |
| 235 | ep++; |
| 236 | s = ep; |
| 237 | gidlist[gidlen++] = strtoul (s, &ep, 10); |
| 238 | } |
| 239 | *gidlenp = gidlen; |
| 240 | |
| 241 | return NSS_STATUS_SUCCESS; |
| 242 | } |
| 243 | |
| 244 | enum nss_status |
| 245 | _nss_nisplus_netname2user (char netname[MAXNETNAMELEN + 1], uid_t *uidp, |
| 246 | gid_t *gidp, int *gidlenp, gid_t *gidlist, int *errnop) |
| 247 | { |
| 248 | char *domain; |
| 249 | nis_result *res; |
| 250 | char sname[NIS_MAXNAMELEN + 2]; /* search criteria + table name */ |
| 251 | size_t slen; |
| 252 | char principal[NIS_MAXNAMELEN + 1]; |
| 253 | int len; |
| 254 | |
| 255 | /* 1. Get home domain of user. */ |
| 256 | domain = strchr (netname, '@'); |
| 257 | if (! domain) |
| 258 | return NSS_STATUS_UNAVAIL; |
| 259 | |
| 260 | ++domain; /* skip '@' */ |
| 261 | |
| 262 | /* 2. Get user's nisplus principal name. */ |
| 263 | slen = snprintf (sname, NIS_MAXNAMELEN, |
| 264 | "[auth_name=%s,auth_type=DES],cred.org_dir.%s" , |
| 265 | netname, domain); |
| 266 | |
| 267 | if (slen >= NIS_MAXNAMELEN) |
| 268 | { |
| 269 | *errnop = EINVAL; |
| 270 | return NSS_STATUS_UNAVAIL; |
| 271 | } |
| 272 | |
| 273 | if (sname[slen - 1] != '.') |
| 274 | { |
| 275 | sname[slen++] = '.'; |
| 276 | sname[slen] = '\0'; |
| 277 | } |
| 278 | |
| 279 | /* must use authenticated call here */ |
| 280 | /* XXX but we cant, for now. XXX */ |
| 281 | res = nis_list (sname, USE_DGRAM+NO_AUTHINFO+FOLLOW_LINKS+FOLLOW_PATH, |
| 282 | NULL, NULL); |
| 283 | if (res == NULL) |
| 284 | { |
| 285 | *errnop = ENOMEM; |
| 286 | return NSS_STATUS_TRYAGAIN; |
| 287 | } |
| 288 | switch (res->status) |
| 289 | { |
| 290 | case NIS_SUCCESS: |
| 291 | case NIS_S_SUCCESS: |
| 292 | break; /* go and do something useful */ |
| 293 | case NIS_NOTFOUND: |
| 294 | case NIS_PARTIAL: |
| 295 | case NIS_NOSUCHNAME: |
| 296 | case NIS_NOSUCHTABLE: |
| 297 | nis_freeresult (res); |
| 298 | return NSS_STATUS_NOTFOUND; |
| 299 | case NIS_S_NOTFOUND: |
| 300 | case NIS_TRYAGAIN: |
| 301 | syslog (LOG_ERR, _("netname2user: (nis+ lookup): %s\n" ), |
| 302 | nis_sperrno (res->status)); |
| 303 | nis_freeresult (res); |
| 304 | *errnop = errno; |
| 305 | return NSS_STATUS_TRYAGAIN; |
| 306 | default: |
| 307 | syslog (LOG_ERR, _("netname2user: (nis+ lookup): %s\n" ), |
| 308 | nis_sperrno (res->status)); |
| 309 | nis_freeresult (res); |
| 310 | return NSS_STATUS_UNAVAIL; |
| 311 | } |
| 312 | |
| 313 | if (NIS_RES_NUMOBJ (res) > 1) |
| 314 | /* |
| 315 | * A netname belonging to more than one principal? |
| 316 | * Something wrong with cred table. should be unique. |
| 317 | * Warn user and continue. |
| 318 | */ |
| 319 | syslog (LOG_ALERT, |
| 320 | _("netname2user: DES entry for %s in directory %s not unique" ), |
| 321 | netname, domain); |
| 322 | |
| 323 | len = ENTRY_LEN (NIS_RES_OBJECT (res), 0); |
| 324 | strncpy (principal, ENTRY_VAL (NIS_RES_OBJECT (res), 0), len); |
| 325 | principal[len] = '\0'; |
| 326 | nis_freeresult (res); |
| 327 | |
| 328 | if (principal[0] == '\0') |
| 329 | return NSS_STATUS_UNAVAIL; |
| 330 | |
| 331 | /* |
| 332 | * 3. Use principal name to look up uid/gid information in |
| 333 | * LOCAL entry in **local** cred table. |
| 334 | */ |
| 335 | domain = nis_local_directory (); |
| 336 | if (strlen (principal) + strlen (domain) + 45 > (size_t) NIS_MAXNAMELEN) |
| 337 | { |
| 338 | syslog (LOG_ERR, _("netname2user: principal name `%s' too long" ), |
| 339 | principal); |
| 340 | return NSS_STATUS_UNAVAIL; |
| 341 | } |
| 342 | |
| 343 | slen = snprintf (sname, sizeof (sname), |
| 344 | "[cname=%s,auth_type=LOCAL],cred.org_dir.%s" , |
| 345 | principal, domain); |
| 346 | |
| 347 | if (sname[slen - 1] != '.') |
| 348 | { |
| 349 | sname[slen++] = '.'; |
| 350 | sname[slen] = '\0'; |
| 351 | } |
| 352 | |
| 353 | /* must use authenticated call here */ |
| 354 | /* XXX but we cant, for now. XXX */ |
| 355 | res = nis_list (sname, USE_DGRAM+NO_AUTHINFO+FOLLOW_LINKS+FOLLOW_PATH, |
| 356 | NULL, NULL); |
| 357 | if (res == NULL) |
| 358 | { |
| 359 | *errnop = ENOMEM; |
| 360 | return NSS_STATUS_TRYAGAIN; |
| 361 | } |
| 362 | switch(res->status) |
| 363 | { |
| 364 | case NIS_NOTFOUND: |
| 365 | case NIS_PARTIAL: |
| 366 | case NIS_NOSUCHNAME: |
| 367 | case NIS_NOSUCHTABLE: |
| 368 | nis_freeresult (res); |
| 369 | return NSS_STATUS_NOTFOUND; |
| 370 | case NIS_S_NOTFOUND: |
| 371 | case NIS_TRYAGAIN: |
| 372 | syslog (LOG_ERR, _("netname2user: (nis+ lookup): %s\n" ), |
| 373 | nis_sperrno (res->status)); |
| 374 | nis_freeresult (res); |
| 375 | *errnop = errno; |
| 376 | return NSS_STATUS_TRYAGAIN; |
| 377 | case NIS_SUCCESS: |
| 378 | case NIS_S_SUCCESS: |
| 379 | break; /* go and do something useful */ |
| 380 | default: |
| 381 | syslog (LOG_ERR, _("netname2user: (nis+ lookup): %s\n" ), |
| 382 | nis_sperrno (res->status)); |
| 383 | nis_freeresult (res); |
| 384 | return NSS_STATUS_UNAVAIL; |
| 385 | } |
| 386 | |
| 387 | if (NIS_RES_NUMOBJ (res) > 1) |
| 388 | /* |
| 389 | * A principal can have more than one LOCAL entry? |
| 390 | * Something wrong with cred table. |
| 391 | * Warn user and continue. |
| 392 | */ |
| 393 | syslog (LOG_ALERT, |
| 394 | _("netname2user: LOCAL entry for %s in directory %s not unique" ), |
| 395 | netname, domain); |
| 396 | /* Fetch the uid */ |
| 397 | *uidp = strtoul (ENTRY_VAL (NIS_RES_OBJECT (res), 2), NULL, 10); |
| 398 | |
| 399 | if (*uidp == 0) |
| 400 | { |
| 401 | syslog (LOG_ERR, _("netname2user: should not have uid 0" )); |
| 402 | nis_freeresult (res); |
| 403 | return NSS_STATUS_NOTFOUND; |
| 404 | } |
| 405 | |
| 406 | parse_grp_str (ENTRY_VAL (NIS_RES_OBJECT (res), 3), |
| 407 | gidp, gidlenp, gidlist, errnop); |
| 408 | |
| 409 | nis_freeresult (res); |
| 410 | return NSS_STATUS_SUCCESS; |
| 411 | } |
| 412 | |