Allow non root processes to authenticate users based on the shadow group
[manu/libnss-mysql-bg.git] / src / nss_support.c
1 /* Copyright (C) 2002 Ben Goodwin
2    This file is part of the nss-mysql library.
3
4    The nss-mysql library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU General Public License as published
6    by the Free Software Foundation; either version 2 of the License, or
7    (at your option) any later version.
8
9    The nss-mysql library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with the nss-mysql library; if not, write to the Free Software
16    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  */
18
19 static const char rcsid[] =
20     "$Id: nss_support.c,v 1.39 2004/11/13 16:25:26 cinergi Exp $";
21
22 #include "nss_mysql.h"
23 #include <string.h>     /* memset() */
24 #include <pwd.h>        /* struct passwd */
25 #ifdef HAVE_SHADOW_H
26 #include <shadow.h>     /* struct spwd */
27 #endif
28 #include <grp.h>        /* struct group */
29
30 extern conf_t conf;
31
32 /* Alignment code adapted from padl.com's nss_ldap */
33 #ifdef __GNUC__
34 #define alignof(ptr) __alignof__(ptr)
35 #else
36 #define alignof(ptr) (sizeof(char *))
37 #endif
38
39 #define align(ptr, blen, TYPE)                                               \
40 do {                                                                         \
41   char *qtr = ptr;                                                           \
42   ptr += alignof(TYPE) - 1;                                                  \
43   ptr -= ((ptr - (char *)NULL) % alignof(TYPE));                             \
44   blen -= (ptr - qtr);                                                       \
45 } while (0)
46
47 /* Password */
48 #if defined(__FreeBSD__)
49 typedef enum {
50   ROW_PW_NAME,
51   ROW_PW_PASSWD,
52   ROW_PW_UID,
53   ROW_PW_GID,
54   ROW_PW_CHANGE,
55   ROW_PW_CLASS,
56   ROW_PW_GECOS,
57   ROW_PW_DIR,
58   ROW_PW_SHELL,
59   ROW_PW_EXPIRE,
60   NUM_PW_ELEMENTS
61 } pw_rows;
62 #elif defined (sun)
63 typedef enum {
64   ROW_PW_NAME,
65   ROW_PW_PASSWD,
66   ROW_PW_UID,
67   ROW_PW_GID,
68   ROW_PW_AGE,
69   ROW_PW_COMMENT,
70   ROW_PW_GECOS,
71   ROW_PW_DIR,
72   ROW_PW_SHELL,
73   NUM_PW_ELEMENTS
74 } pw_rows;
75 #else
76 typedef enum {
77   ROW_PW_NAME,
78   ROW_PW_PASSWD,
79   ROW_PW_UID,
80   ROW_PW_GID,
81   ROW_PW_GECOS,
82   ROW_PW_DIR,
83   ROW_PW_SHELL,
84   NUM_PW_ELEMENTS
85 } pw_rows;
86 #endif
87
88 /* Shadow */
89 typedef enum {
90   ROW_SP_NAMP,
91   ROW_SP_PWDP,
92   ROW_SP_LSTCHG,
93   ROW_SP_MIN,
94   ROW_SP_MAX,
95   ROW_SP_WARN,
96   ROW_SP_INACT,
97   ROW_SP_EXPIRE,
98   ROW_SP_FLAG,
99   NUM_SP_ELEMENTS
100 } sp_rows;
101
102 /* Group */
103 typedef enum {
104   ROW_GR_NAME,
105   ROW_GR_PASSWD,
106   ROW_GR_GID,
107   ROW_GR_MEM,
108   NUM_GR_ELEMENTS
109 } gr_rows;
110
111 NSS_STATUS 
112 _nss_mysql_load_passwd (void *result, char *buffer, size_t buflen,
113                         MYSQL_RES *mresult, int *errnop)
114 {
115   DN ("_nss_mysql_load_passwd")
116   MYSQL_ROW row;
117   int retVal, i;
118   struct passwd *pw = (struct passwd *)result;
119   size_t offsets[NUM_PW_ELEMENTS];
120   unsigned long *lengths;
121   unsigned int num_fields;
122
123   DENTER
124   retVal = _nss_mysql_fetch_row (&row, mresult);
125   if (retVal != NSS_SUCCESS)
126     DSRETURN (retVal)
127   num_fields = _nss_mysql_num_fields (mresult);
128   if (num_fields != NUM_PW_ELEMENTS)
129     {
130       _nss_mysql_log (LOG_ALERT,
131                       "mysql_fetch_row() found %u rows (expecting %u).",
132                       num_fields, NUM_PW_ELEMENTS);
133       DSRETURN (NSS_UNAVAIL)
134     }
135
136   /* Make sure we have enough room in 'buffer' for all our data */
137   lengths = (unsigned long *) _nss_mysql_fetch_lengths (mresult);
138   offsets[ROW_PW_NAME] = 0;
139   for (i = 1; i < NUM_PW_ELEMENTS; i++)
140     offsets[i] = offsets[i - 1] + lengths[i - 1] + 1;
141   if (offsets[NUM_PW_ELEMENTS - 1] > buflen)
142     EXHAUSTED_BUFFER;
143
144   /* Clear out buffer and copy in data */
145   memset (buffer, 0, buflen);
146   pw->pw_name = memcpy (buffer + offsets[ROW_PW_NAME], row[ROW_PW_NAME],
147                         lengths[ROW_PW_NAME]);
148   pw->pw_passwd = memcpy (buffer + offsets[ROW_PW_PASSWD], row[ROW_PW_PASSWD],
149                           lengths[ROW_PW_PASSWD]);
150   pw->pw_uid = atoi (row[ROW_PW_UID]);
151   pw->pw_gid = atoi (row[ROW_PW_GID]);
152   pw->pw_gecos = memcpy (buffer + offsets[ROW_PW_GECOS], row[ROW_PW_GECOS],
153                          lengths[ROW_PW_GECOS]);
154   pw->pw_dir = memcpy (buffer + offsets[ROW_PW_DIR], row[ROW_PW_DIR],
155                        lengths[ROW_PW_DIR]);
156   pw->pw_shell = memcpy (buffer + offsets[ROW_PW_SHELL], row[ROW_PW_SHELL],
157                          lengths[ROW_PW_SHELL]);
158 #if defined(__FreeBSD__)
159   pw->pw_change = atoi (row[ROW_PW_CHANGE]);
160   pw->pw_class = memcpy (buffer + offsets[ROW_PW_CLASS], row[ROW_PW_CLASS],
161                          lengths[ROW_PW_CLASS]);
162   pw->pw_change = atoi (row[ROW_PW_EXPIRE]);
163 #endif
164
165 #if defined(sun)
166   pw->pw_age = memcpy (buffer + offsets[ROW_PW_AGE], row[ROW_PW_AGE],
167                        lengths[ROW_PW_AGE]);
168   pw->pw_comment = memcpy (buffer + offsets[ROW_PW_COMMENT],
169                            row[ROW_PW_COMMENT], lengths[ROW_PW_COMMENT]);
170 #endif
171
172   DSRETURN (NSS_SUCCESS)
173 }
174
175 #ifdef HAVE_SHADOW_H
176 NSS_STATUS 
177 _nss_mysql_load_shadow (void *result, char *buffer, size_t buflen,
178                         MYSQL_RES *mresult, int *errnop)
179 {
180   DN ("_nss_mysql_load_shadow")
181   MYSQL_ROW row;
182   int retVal;
183   struct spwd *sp = (struct spwd *)result;
184   size_t offsets[NUM_SP_ELEMENTS];
185   unsigned long *lengths;
186   unsigned int num_fields;
187
188   DENTER
189   retVal = _nss_mysql_fetch_row (&row, mresult);
190   if (retVal != NSS_SUCCESS)
191     DSRETURN (retVal)
192   num_fields = _nss_mysql_num_fields (mresult);
193   if (num_fields != NUM_SP_ELEMENTS)
194     {
195       _nss_mysql_log (LOG_ALERT,
196                       "mysql_fetch_row() found %u rows (expecting %u).",
197                       num_fields, NUM_SP_ELEMENTS);
198       DSRETURN (NSS_UNAVAIL)
199     }
200
201   /* Make sure we have enough room in 'buffer' for all our data */
202   lengths = (unsigned long *) _nss_mysql_fetch_lengths (mresult);
203   offsets[ROW_SP_NAMP] = 0;
204   offsets[ROW_SP_PWDP] = offsets[ROW_SP_NAMP] + lengths[ROW_SP_NAMP] + 1;
205   if (offsets[ROW_SP_PWDP] + lengths[ROW_SP_PWDP] + 1 > buflen)
206     EXHAUSTED_BUFFER;
207
208   /* Clear out buffer and copy in data */
209   memset (buffer, 0, buflen);
210   sp->sp_namp = memcpy (buffer + offsets[ROW_SP_NAMP], row[ROW_SP_NAMP],
211                         lengths[ROW_SP_NAMP]);
212   sp->sp_pwdp = memcpy (buffer + offsets[ROW_SP_PWDP], row[ROW_SP_PWDP],
213                         lengths[ROW_SP_PWDP]);
214   sp->sp_lstchg = atol (row[ROW_SP_LSTCHG]);
215   sp->sp_min = atol (row[ROW_SP_MIN]);
216   sp->sp_max = atol (row[ROW_SP_MAX]);
217   sp->sp_warn = atol (row[ROW_SP_WARN]);
218   sp->sp_inact = atol (row[ROW_SP_INACT]);
219   sp->sp_expire = atol (row[ROW_SP_EXPIRE]);
220   sp->sp_flag = (unsigned long) atol (row[ROW_SP_FLAG]);
221   DSRETURN (NSS_SUCCESS)
222 }
223 #endif
224
225 static NSS_STATUS
226 _nss_mysql_load_memsbygid (void *result, char *buffer, size_t buflen,
227                            MYSQL_RES *mresult, int *errnop)
228 {
229   DN ("_nss_mysql_load_memsbygid")
230   MYSQL_ROW row;
231   int retVal;
232   struct group *gr = (struct group *)result;
233   char **members;
234   unsigned long num_rows, i;
235   unsigned long *lengths;
236   size_t strings_offset;
237
238   DENTER
239   num_rows = _nss_mysql_num_rows (mresult);
240   align (buffer, buflen, char *);
241
242   /* Return empty/NULL group list if no members */
243   if (num_rows == 0)
244     {
245       gr->gr_mem = (char **) (uintptr_t)buffer;
246       DSRETURN (NSS_SUCCESS)
247     }
248
249   members = (char **)buffer;
250   strings_offset = (num_rows + 1) * sizeof (char *);
251   /* Allow room for NUM_ROWS + 1 pointers */
252   if (strings_offset > buflen)
253     EXHAUSTED_BUFFER;
254
255   buflen -= strings_offset;
256
257   /* Load the first one */
258   retVal = _nss_mysql_fetch_row (&row, mresult);
259   if (retVal != NSS_SUCCESS)
260     DSRETURN (retVal)
261   lengths = (unsigned long *) _nss_mysql_fetch_lengths (mresult);
262   if (lengths[0] + 1 > buflen)
263     EXHAUSTED_BUFFER;
264
265   members[0] = buffer + strings_offset;
266   strncpy (members[0], row[0], lengths[0]);
267   buflen -= lengths[0] + 1;
268
269   /* Load the rest */
270   for (i = 1; i < num_rows; i++)
271     {
272       /* Set pointer to one byte after the last string loaded */
273       members[i] = members[i - 1] + lengths[0] + 1;
274
275       retVal = _nss_mysql_fetch_row (&row, mresult);
276       if (retVal != NSS_SUCCESS)
277         DSRETURN (retVal)
278       lengths = (unsigned long *) _nss_mysql_fetch_lengths (mresult);
279       if (lengths[0] + 1 > buflen)
280         EXHAUSTED_BUFFER;
281       strncpy (members[i], row[0], lengths[0]);
282       buflen -= lengths[0] + 1;
283     }
284
285   /* Set the last pointer to NULL to terminate the pointer-list */
286   members[num_rows] = NULL;
287
288   /* Set gr->gr_mem to point to start of our pointer-list */
289   gr->gr_mem = (char **) (uintptr_t)buffer;
290
291   DSRETURN (NSS_SUCCESS)
292 }
293
294 NSS_STATUS 
295 _nss_mysql_load_group (void *result, char *buffer, size_t buflen,
296                        MYSQL_RES *mresult, int *errnop)
297 {
298   DN ("_nss_mysql_load_group")
299   MYSQL_ROW row;
300   MYSQL_RES *mresult_grmem = NULL;
301   int retVal;
302   struct group *gr = (struct group *)result;
303   size_t offsets[NUM_GR_ELEMENTS];
304   unsigned long *lengths;
305   unsigned int num_fields;
306
307   DENTER
308   retVal = _nss_mysql_fetch_row (&row, mresult);
309   if (retVal != NSS_SUCCESS)
310     DSRETURN (retVal)
311   num_fields = _nss_mysql_num_fields (mresult);
312   if (num_fields != NUM_GR_ELEMENTS - 1) /* gr_mem not part of this query */
313     {
314       _nss_mysql_log (LOG_ALERT,
315                       "mysql_fetch_row() found %u rows (expecting %u).",
316                       num_fields, NUM_GR_ELEMENTS - 1);
317       DSRETURN (NSS_UNAVAIL)
318     }
319
320   /*
321    * Make sure we have enough room in 'buffer' for all our data
322    * (not including gr_mem - that's dealt with later
323    */
324   lengths = (unsigned long *) _nss_mysql_fetch_lengths (mresult);
325   offsets[ROW_GR_NAME] = 0;
326   offsets[ROW_GR_PASSWD] = offsets[ROW_GR_NAME] + lengths[ROW_GR_NAME] + 1;
327   offsets[ROW_GR_MEM] = offsets[ROW_GR_PASSWD] + lengths[ROW_GR_PASSWD] + 1;
328   if (offsets[ROW_GR_MEM] + 1 > buflen)
329     EXHAUSTED_BUFFER;
330
331   /* Clear out buffer and copy in data (except gr_mem) */
332   memset (buffer, 0, buflen);
333   gr->gr_name = memcpy (buffer + offsets[ROW_GR_NAME], row[ROW_GR_NAME],
334                         lengths[ROW_GR_NAME]);
335   gr->gr_passwd = memcpy (buffer + offsets[ROW_GR_PASSWD], row[ROW_GR_PASSWD],
336                           lengths[ROW_GR_PASSWD]);
337   gr->gr_gid = atoi (row[ROW_GR_GID]);
338
339   /* Load gr_mem */
340   retVal = _nss_mysql_lookup (BYNUM, NULL, gr->gr_gid,
341                               conf.sql.query.memsbygid, nfalse, result,
342                               buffer + offsets[ROW_GR_MEM],
343                               buflen - offsets[ROW_GR_MEM],
344                               errnop, _nss_mysql_load_memsbygid,
345                               &mresult_grmem, FUNCNAME);
346
347   DSRETURN (retVal)
348 }
349
350 NSS_STATUS
351 _nss_mysql_load_gidsbymem (void *result, char *buffer, size_t buflen,
352                            MYSQL_RES *mresult, int *errnop)
353 {
354   DN ("_nss_mysql_load_gidsbymem")
355   MYSQL_ROW row;
356   unsigned long num_rows, i;
357   group_info_t *gi = (group_info_t *)result;
358   gid_t *groups;
359   int retVal;
360   gid_t gid;
361
362   DENTER
363   num_rows = _nss_mysql_num_rows (mresult);
364
365   /* Nothing to load = success */
366   if (num_rows == 0)
367     DSRETURN (NSS_SUCCESS)
368
369   /* If we need more room and we're allowed to alloc it, alloc it */
370   if (num_rows + *gi->start > *gi->size)
371     {
372       long int newsize = *gi->size;
373
374       if (gi->limit <= 0)                /* Allocate as much as we need */
375         newsize = num_rows + *gi->start;
376       else if (*gi->size != gi->limit)   /* Allocate to limit */
377         newsize = gi->limit;
378
379       if (newsize != *gi->size)          /* If we've got more room, do it */
380         {
381           gid_t *groups = *gi->groupsp;
382           gid_t *newgroups;
383
384           newgroups = realloc (groups, newsize * sizeof (*groups));
385           if (newgroups != NULL)
386             {
387               *gi->groupsp = groups = newgroups;
388               *gi->size = newsize;
389             }
390         }
391     }
392
393   groups = *gi->groupsp;
394   for (i = *gi->start; i < *gi->size; i++)
395     {
396       retVal = _nss_mysql_fetch_row (&row, mresult);
397       if (retVal != NSS_SUCCESS)
398         DSRETURN (retVal)
399       gid = atoi (row[0]);
400       if ((long int)gid != gi->group && (long int)gid != groups[0])
401         groups[(*gi->start)++] = gid;
402     }
403
404   DSRETURN (NSS_SUCCESS)
405 }
406
407 /* Thanks to Clement Laforet for most of this */
408 #if defined(__FreeBSD__)
409
410 NSS_METHOD_PROTOTYPE(__nss_compat_getpwnam_r);
411 NSS_METHOD_PROTOTYPE(__nss_compat_getpwuid_r);
412 NSS_METHOD_PROTOTYPE(__nss_compat_getpwent_r);
413 NSS_METHOD_PROTOTYPE(__nss_compat_setpwent);
414 NSS_METHOD_PROTOTYPE(__nss_compat_endpwent);
415 NSS_METHOD_PROTOTYPE(__nss_compat_getgrnam_r);
416 NSS_METHOD_PROTOTYPE(__nss_compat_getgrgid_r);
417 NSS_METHOD_PROTOTYPE(__nss_compat_getgrent_r);
418 NSS_METHOD_PROTOTYPE(__nss_compat_setgrent);
419 NSS_METHOD_PROTOTYPE(__nss_compat_endgrent);
420
421 NSS_STATUS _nss_mysql_getpwnam_r (const char *, struct passwd *, char *,
422                                   size_t, int *);
423 NSS_STATUS _nss_mysql_getpwuid_r (uid_t, struct passwd *, char *,
424                                   size_t, int *);
425 NSS_STATUS _nss_mysql_getpwent_r (struct passwd *, char *, size_t, int *);
426 NSS_STATUS _nss_mysql_setpwent (void);
427 NSS_STATUS _nss_mysql_endpwent (void);
428
429 NSS_STATUS _nss_mysql_getgrnam_r (const char *, struct group *, char *,
430                                   size_t, int *);
431 NSS_STATUS _nss_mysql_getgrgid_r (gid_t, struct group *, char *,
432                                   size_t, int *);
433 NSS_STATUS _nss_mysql_getgrent_r (struct group *, char *, size_t, int *);
434 NSS_STATUS _nss_mysql_setgrent (void);
435 NSS_STATUS _nss_mysql_endgrent (void);
436
437 static ns_mtab methods[] = {
438     { NSDB_PASSWD, "getpwnam_r", __nss_compat_getpwnam_r, _nss_mysql_getpwnam_r },
439     { NSDB_PASSWD, "getpwuid_r", __nss_compat_getpwuid_r, _nss_mysql_getpwuid_r },
440     { NSDB_PASSWD, "getpwent_r", __nss_compat_getpwent_r, _nss_mysql_getpwent_r },
441     { NSDB_PASSWD, "endpwent",   __nss_compat_setpwent,   _nss_mysql_setpwent },
442     { NSDB_PASSWD, "setpwent",   __nss_compat_endpwent,   _nss_mysql_endpwent },
443     { NSDB_GROUP,  "getgrnam_r", __nss_compat_getgrnam_r, _nss_mysql_getgrnam_r },
444     { NSDB_GROUP,  "getgrgid_r", __nss_compat_getgrgid_r, _nss_mysql_getgrgid_r },
445     { NSDB_GROUP,  "getgrent_r", __nss_compat_getgrent_r, _nss_mysql_getgrent_r },
446     { NSDB_GROUP,  "endgrent",   __nss_compat_setgrent,   _nss_mysql_setgrent },
447     { NSDB_GROUP,  "setgrent",   __nss_compat_endgrent,   _nss_mysql_endgrent },
448 };
449
450 ns_mtab *
451 nss_module_register (const char *name, unsigned int *size,
452                      nss_module_unregister_fn *unregister)
453 {
454     *size = sizeof (methods) / sizeof (methods[0]);
455     *unregister = NULL;
456     return (methods);
457 }
458
459 #endif /* defined(__FreeBSD__) */
460