1 /*
2 * $Id$
3 *
4 * This function provides a thread safer version of getgrgid() for use
5 * with PAM modules that care about this sort of thing.
6 *
7 * XXX - or at least it should provide a thread-safe alternative.
8 */
9
10 #include "pam_modutil_private.h"
11
12 #include <errno.h>
13 #include <limits.h>
14 #include <grp.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17
18 static int intlen(int number)
19 {
20 int len = 2;
21 while (number != 0) {
22 number /= 10;
23 len++;
24 }
25 return len;
26 }
27
28 static int longlen(long number)
29 {
30 int len = 2;
31 while (number != 0) {
32 number /= 10;
33 len++;
34 }
35 return len;
36 }
37
38 struct group *
39 pam_modutil_getgrgid(pam_handle_t *pamh, gid_t gid)
40 {
41 #ifdef HAVE_GETGRGID_R
42
43 void *buffer=NULL;
44 size_t length = PWD_INITIAL_LENGTH;
45
46 do {
47 int status;
48 void *new_buffer;
49 struct group *result = NULL;
50
51 new_buffer = realloc(buffer, sizeof(struct group) + length);
52 if (new_buffer == NULL) {
53
54 D(("out of memory"));
55
56 /* no memory for the user - so delete the memory */
57 if (buffer) {
58 free(buffer);
59 }
60 return NULL;
61 }
62 buffer = new_buffer;
63
64 /* make the re-entrant call to get the grp structure */
65 errno = 0;
66 status = getgrgid_r(gid, buffer,
67 sizeof(struct group) + (char *) buffer,
68 length, &result);
69 if (!status && (result == buffer)) {
70 char *data_name;
71 const void *ignore;
72 int i;
73
74 data_name = malloc(strlen("_pammodutil_getgrgid") + 1 +
75 longlen((long)gid) + 1 + intlen(INT_MAX) + 1);
76 if ((pamh != NULL) && (data_name == NULL)) {
77 D(("was unable to register the data item [%s]",
78 pam_strerror(pamh, status)));
79 free(buffer);
80 return NULL;
81 }
82
83 if (pamh != NULL) {
84 for (i = 0; i < INT_MAX; i++) {
85 sprintf(data_name, "_pammodutil_getgrgid_%ld_%d",
86 (long) gid, i);
87 status = PAM_NO_MODULE_DATA;
88 if (pam_get_data(pamh, data_name, &ignore) != PAM_SUCCESS) {
89 status = pam_set_data(pamh, data_name,
90 result, pam_modutil_cleanup);
91 }
92 if (status == PAM_SUCCESS) {
93 break;
94 }
95 }
96 } else {
97 status = PAM_SUCCESS;
98 }
99
100 free(data_name);
101
102 if (status == PAM_SUCCESS) {
103 D(("success"));
104 return result;
105 }
106
107 D(("was unable to register the data item [%s]",
108 pam_strerror(pamh, status)));
109
110 free(buffer);
111 return NULL;
112
113 } else if (errno != ERANGE && errno != EINTR) {
114 /* no sense in repeating the call */
115 break;
116 }
117
118 length <<= PWD_LENGTH_SHIFT;
119
120 } while (length < PWD_ABSURD_PWD_LENGTH);
121
122 D(("grp structure took %u bytes or so of memory",
123 length+sizeof(struct group)));
124
125 free(buffer);
126 return NULL;
127
128 #else /* ie. ifndef HAVE_GETGRGID_R */
129
130 /*
131 * Sorry, there does not appear to be a reentrant version of
132 * getgrgid(). So, we use the standard libc function.
133 */
134
135 return getgrgid(gid);
136
137 #endif /* def HAVE_GETGRGID_R */
138 }