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>
29extern 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. */
34enum 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
117enum 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, ... */
210static enum nss_status
211parse_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
244enum 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