Fix "buffer to small" problems and memory leaks.
[kopensolaris-gnu/glibc.git] / nis / nss_nisplus / nisplus-publickey.c
1 /* Copyright (c) 1997 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3    Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.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 Library General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    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    Library General Public License for more details.
14
15    You should have received a copy of the GNU Library General Public
16    License along with the GNU C Library; see the file COPYING.LIB.  If not,
17    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18    Boston, MA 02111-1307, USA.  */
19
20 #include <nss.h>
21 #include <ctype.h>
22 #include <errno.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <syslog.h>
26 #include <rpc/rpc.h>
27 #include <rpcsvc/nis.h>
28 #include <rpcsvc/nislib.h>
29 #ifdef HAVE_SECURE_RPC
30 #include <rpc/key_prot.h>
31 extern int xdecrypt (char *, char *);
32 #endif
33
34 #include <nss-nisplus.h>
35
36 /* If we haven't found the entry, we give a SUCCESS and an empty key back. */
37 enum nss_status
38 _nss_nisplus_getpublickey (const char *netname, char *pkey)
39 {
40   nis_result *res;
41   enum nss_status retval;
42   char buf[NIS_MAXNAMELEN+2];
43   char *domain, *cptr;
44   int len;
45
46   pkey[0] = 0;
47
48   if (netname == NULL)
49     {
50       __set_errno (EINVAL);
51       return NSS_STATUS_UNAVAIL;
52     }
53
54   domain = strchr (netname, '@');
55   if (!domain)
56     return NSS_STATUS_UNAVAIL;
57   domain++;
58
59   snprintf (buf, NIS_MAXNAMELEN,
60             "[auth_name=%s,auth_type=DES],cred.org_dir.%s",
61             netname, domain);
62
63   if (buf[strlen (buf)-1] != '.')
64     strcat (buf, ".");
65
66   res = nis_list (buf, USE_DGRAM+NO_AUTHINFO+FOLLOW_LINKS+FOLLOW_PATH,
67                   NULL, NULL);
68
69   retval = niserr2nss (res->status);
70
71   if (retval != NSS_STATUS_SUCCESS)
72     {
73       if (retval == NSS_STATUS_TRYAGAIN)
74         __set_errno (EAGAIN);
75       if (res->status == NIS_NOTFOUND)
76         retval = NSS_STATUS_SUCCESS;
77       nis_freeresult (res);
78       return retval;
79     }
80
81   if (res->objects.objects_len > 1)
82     {
83       /*
84        * More than one principal with same uid?
85        * something wrong with cred table. Should be unique
86        * Warn user and continue.
87        */
88       printf (_("DES entry for netname %s not unique\n"), netname);
89       nis_freeresult (res);
90       return NSS_STATUS_SUCCESS;
91     }
92
93   len = ENTRY_LEN (res->objects.objects_val, 3);
94   memcpy (pkey, ENTRY_VAL (res->objects.objects_val,3), len);
95   pkey[len] = 0;
96   cptr = strchr (pkey, ':');
97   if (cptr)
98     cptr[0] = '\0';
99   nis_freeresult (res);
100
101   return NSS_STATUS_SUCCESS;
102 }
103
104 enum nss_status
105 _nss_nisplus_getsecretkey (const char *netname, char *skey, char *passwd)
106 {
107 #ifdef HAVE_SECURE_RPC
108   nis_result *res;
109   enum nss_status retval;
110   char buf[NIS_MAXNAMELEN+2];
111   char *domain, *cptr;
112   int len;
113
114   skey[0] = 0;
115
116   if (netname == NULL)
117     {
118       __set_errno (EINVAL);
119       return NSS_STATUS_UNAVAIL;
120     }
121
122   domain = strchr (netname, '@');
123   if (!domain)
124     return NSS_STATUS_UNAVAIL;
125   domain++;
126
127   snprintf (buf, NIS_MAXNAMELEN,
128             "[auth_name=%s,auth_type=DES],cred.org_dir.%s",
129             netname, domain);
130
131   if (buf[strlen(buf)-1] != '.')
132     strcat(buf, ".");
133
134   res = nis_list (buf, USE_DGRAM+NO_AUTHINFO+FOLLOW_LINKS+FOLLOW_PATH,
135                   NULL, NULL);
136
137   retval = niserr2nss (res->status);
138
139   if (retval != NSS_STATUS_SUCCESS)
140     {
141       if (retval == NSS_STATUS_TRYAGAIN)
142         __set_errno (EAGAIN);
143       nis_freeresult (res);
144       return retval;
145     }
146
147   if (res->objects.objects_len > 1)
148     {
149       /*
150        * More than one principal with same uid?
151        * something wrong with cred table. Should be unique
152        * Warn user and continue.
153        */
154       printf (_("DES entry for netname %s not unique\n"), netname);
155       nis_freeresult (res);
156       return NSS_STATUS_SUCCESS;
157     }
158
159   len = ENTRY_LEN (res->objects.objects_val, 4);
160   memcpy (buf, ENTRY_VAL (res->objects.objects_val,4), len);
161   skey[len] = 0;
162   cptr = strchr (skey, ':');
163   if (cptr)
164     cptr[0] = '\0';
165   nis_freeresult (res);
166
167   if (!xdecrypt (buf, passwd))
168     return NSS_STATUS_SUCCESS;
169
170   if (memcmp (buf, &(buf[HEXKEYBYTES]), KEYCHECKSUMSIZE) != 0)
171     return NSS_STATUS_SUCCESS;
172
173   buf[HEXKEYBYTES] = 0;
174   strcpy (skey, buf);
175 #else
176   skey[0] = 0;
177 #endif
178
179   return NSS_STATUS_SUCCESS;
180 }
181
182 /* Parse information from the passed string.
183    The format of the string passed is gid,grp,grp, ...  */
184 static enum nss_status
185 parse_grp_str (const char *s, gid_t *gidp, int *gidlenp, gid_t *gidlist)
186 {
187   int gidlen;
188
189   if (!s || (!isdigit (*s)))
190     {
191       syslog (LOG_ERR, "netname2user: missing group id list in '%s'.", s);
192       return NSS_STATUS_NOTFOUND;
193     }
194
195   *gidp = atoi (s);
196
197   gidlen = 0;
198
199   while ((s = strchr (s, ',')) != NULL)
200     {
201       s++;
202       gidlist[gidlen++] = atoi (s);
203     }
204   *gidlenp = gidlen;
205
206   return NSS_STATUS_SUCCESS;
207 }
208
209 enum nss_status
210 _nss_nisplus_netname2user (char netname[MAXNETNAMELEN + 1], uid_t *uidp,
211                        gid_t *gidp, int *gidlenp, gid_t *gidlist)
212 {
213   char *domain;
214   nis_result *res;
215   char sname[NIS_MAXNAMELEN+1]; /*  search criteria + table name */
216   char principal[NIS_MAXNAMELEN+1];
217   int len;
218
219   /* 1.  Get home domain of user. */
220   domain = strchr (netname, '@');
221   if (! domain)
222     return NSS_STATUS_UNAVAIL;
223
224   domain++;  /* skip '@' */
225
226   /* 2.  Get user's nisplus principal name.  */
227   if ((strlen (netname) + strlen (domain)+45) >
228       (size_t) NIS_MAXNAMELEN)
229     return NSS_STATUS_UNAVAIL;
230
231   snprintf (sname, NIS_MAXNAMELEN,
232             "[auth_name=%s,auth_type=DES],cred.org_dir.%s",
233             netname, domain);
234   if (sname[strlen (sname) - 1] != '.')
235     strcat(sname, ".");
236
237   /* must use authenticated call here */
238   /* XXX but we cant, for now. XXX */
239   res = nis_list (sname, USE_DGRAM+NO_AUTHINFO+FOLLOW_LINKS+FOLLOW_PATH,
240                   NULL, NULL);
241   switch (res->status)
242     {
243     case NIS_SUCCESS:
244     case NIS_S_SUCCESS:
245       break;   /* go and do something useful */
246     case NIS_NOTFOUND:
247     case NIS_PARTIAL:
248     case NIS_NOSUCHNAME:
249     case NIS_NOSUCHTABLE:
250       nis_freeresult (res);
251       return NSS_STATUS_NOTFOUND;
252     case NIS_S_NOTFOUND:
253     case NIS_TRYAGAIN:
254       syslog (LOG_ERR, "netname2user: (nis+ lookup): %s\n",
255               nis_sperrno (res->status));
256       nis_freeresult (res);
257       return NSS_STATUS_TRYAGAIN;
258     default:
259       syslog (LOG_ERR, "netname2user: (nis+ lookup): %s\n",
260               nis_sperrno (res->status));
261       nis_freeresult (res);
262       return NSS_STATUS_UNAVAIL;
263     }
264
265   if (res->objects.objects_len > 1)
266     {
267       /*
268        * A netname belonging to more than one principal?
269        * Something wrong with cred table. should be unique.
270        * Warn user and continue.
271        */
272       syslog (LOG_ALERT,
273               _("netname2user: DES entry for %s in directory %s not unique"),
274               netname, domain);
275     }
276
277   len = ENTRY_LEN (res->objects.objects_val, 0);
278   strncpy (principal, ENTRY_VAL (res->objects.objects_val, 0), len);
279   principal[len] = '\0';
280   nis_freeresult (res);
281
282   if (principal[0] == '\0')
283     return NSS_STATUS_UNAVAIL;
284
285   /*
286    *      3.  Use principal name to look up uid/gid information in
287    *      LOCAL entry in **local** cred table.
288    */
289   domain = nis_local_directory ();
290   if ((strlen (principal)+strlen (domain)+45) >
291       (size_t) NIS_MAXNAMELEN)
292     {
293       syslog (LOG_ERR, _("netname2user: principal name '%s' too long"),
294               principal);
295       return NSS_STATUS_UNAVAIL;
296     }
297   sprintf (sname, "[cname=%s,auth_type=LOCAL],cred.org_dir.%s",
298           principal, domain);
299   if (sname[strlen(sname) - 1] != '.')
300     strcat(sname, ".");
301
302   /* must use authenticated call here */
303   /* XXX but we cant, for now. XXX */
304   res = nis_list (sname, USE_DGRAM+NO_AUTHINFO+FOLLOW_LINKS+FOLLOW_PATH,
305                   NULL, NULL);
306   switch(res->status)
307     {
308     case NIS_NOTFOUND:
309     case NIS_PARTIAL:
310     case NIS_NOSUCHNAME:
311     case NIS_NOSUCHTABLE:
312       nis_freeresult (res);
313       return NSS_STATUS_NOTFOUND;
314     case NIS_S_NOTFOUND:
315     case NIS_TRYAGAIN:
316       syslog (LOG_ERR, "netname2user: (nis+ lookup): %s\n",
317               nis_sperrno (res->status));
318       nis_freeresult (res);
319       return NSS_STATUS_TRYAGAIN;
320     case NIS_SUCCESS:
321     case NIS_S_SUCCESS:
322       break;   /* go and do something useful */
323     default:
324       syslog (LOG_ERR, "netname2user: (nis+ lookup): %s\n",
325               nis_sperrno (res->status));
326       nis_freeresult (res);
327       return NSS_STATUS_UNAVAIL;
328     }
329
330   if (res->objects.objects_len > 1)
331     {
332       /*
333        * A principal can have more than one LOCAL entry?
334        * Something wrong with cred table.
335        * Warn user and continue.
336        */
337       syslog(LOG_ALERT,
338              _("netname2user: LOCAL entry for %s in directory %s not unique"),
339              netname, domain);
340     }
341   /* Fetch the uid */
342   *uidp = atoi (ENTRY_VAL (res->objects.objects_val, 2));
343
344   if (*uidp == 0)
345     {
346       syslog (LOG_ERR, _("netname2user: should not have uid 0"));
347       return NSS_STATUS_NOTFOUND;
348     }
349
350   parse_grp_str (ENTRY_VAL (res->objects.objects_val, 3),
351                  gidp, gidlenp, gidlist);
352
353   nis_freeresult (res);
354   return NSS_STATUS_SUCCESS;
355 }