1 /* Prototype for the setgrent functions we use here.  */
2 typedef enum nss_status (*set_function) (void);
3 
4 /* Prototype for the endgrent functions we use here.  */
5 typedef enum nss_status (*end_function) (void);
6 
7 /* Prototype for the setgrent functions we use here.  */
8 typedef enum nss_status (*get_function) (struct group *, char *,
9 					 size_t, int *);
10 
11 
12 static enum nss_status
compat_call(nss_action_list nip,const char * user,gid_t group,long int * start,long int * size,gid_t ** groupsp,long int limit,int * errnop)13 compat_call (nss_action_list nip, const char *user, gid_t group, long int *start,
14 	     long int *size, gid_t **groupsp, long int limit, int *errnop)
15 {
16   struct group grpbuf;
17   enum nss_status status;
18   set_function setgrent_fct;
19   get_function getgrent_fct;
20   end_function endgrent_fct;
21   gid_t *groups = *groupsp;
22 
23   getgrent_fct = __nss_lookup_function (nip, "getgrent_r");
24   if (getgrent_fct == NULL)
25     return NSS_STATUS_UNAVAIL;
26 
27   setgrent_fct = __nss_lookup_function (nip, "setgrent");
28   if (setgrent_fct)
29     {
30       status = DL_CALL_FCT (setgrent_fct, ());
31       if (status != NSS_STATUS_SUCCESS)
32 	return status;
33     }
34 
35   endgrent_fct = __nss_lookup_function (nip, "endgrent");
36 
37   struct scratch_buffer tmpbuf;
38   scratch_buffer_init (&tmpbuf);
39   enum nss_status result = NSS_STATUS_SUCCESS;
40 
41   do
42     {
43       while ((status = DL_CALL_FCT (getgrent_fct,
44 				     (&grpbuf, tmpbuf.data, tmpbuf.length,
45 				      errnop)),
46 	      status == NSS_STATUS_TRYAGAIN)
47 	     && *errnop == ERANGE)
48         {
49 	  if (!scratch_buffer_grow (&tmpbuf))
50 	    {
51 	      result = NSS_STATUS_TRYAGAIN;
52 	      goto done;
53 	    }
54         }
55 
56       if (status != NSS_STATUS_SUCCESS)
57         goto done;
58 
59       if (grpbuf.gr_gid != group)
60         {
61           char **m;
62 
63           for (m = grpbuf.gr_mem; *m != NULL; ++m)
64             if (strcmp (*m, user) == 0)
65               {
66 		/* Check whether the group is already on the list.  */
67 		long int cnt;
68 		for (cnt = 0; cnt < *start; ++cnt)
69 		  if (groups[cnt] == grpbuf.gr_gid)
70 		    break;
71 
72 		if (cnt == *start)
73 		  {
74 		    /* Matches user and not yet on the list.  Insert
75 		       this group.  */
76 		    if (__glibc_unlikely (*start == *size))
77 		      {
78 			/* Need a bigger buffer.  */
79 			gid_t *newgroups;
80 			long int newsize;
81 
82 			if (limit > 0 && *size == limit)
83 			  /* We reached the maximum.  */
84 			  goto done;
85 
86 			if (limit <= 0)
87 			  newsize = 2 * *size;
88 			else
89 			  newsize = MIN (limit, 2 * *size);
90 
91 			newgroups = realloc (groups,
92 					     newsize * sizeof (*groups));
93 			if (newgroups == NULL)
94 			  goto done;
95 			*groupsp = groups = newgroups;
96 			*size = newsize;
97 		      }
98 
99 		    groups[*start] = grpbuf.gr_gid;
100 		    *start += 1;
101 		  }
102 
103                 break;
104               }
105         }
106     }
107   while (status == NSS_STATUS_SUCCESS);
108 
109  done:
110   scratch_buffer_free (&tmpbuf);
111 
112   if (endgrent_fct)
113     DL_CALL_FCT (endgrent_fct, ());
114 
115   return result;
116 }
117