1 /*
2 * $Id$
3 *
4 * This function provides a thread safer version of getgrnam() 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 struct group *
29 pam_modutil_getgrnam(pam_handle_t *pamh, const char *group)
30 {
31 #ifdef HAVE_GETGRNAM_R
32
33 void *buffer=NULL;
34 size_t length = PWD_INITIAL_LENGTH;
35
36 do {
37 int status;
38 void *new_buffer;
39 struct group *result = NULL;
40
41 new_buffer = realloc(buffer, sizeof(struct group) + length);
42 if (new_buffer == NULL) {
43
44 D(("out of memory"));
45
46 /* no memory for the group - so delete the memory */
47 if (buffer) {
48 free(buffer);
49 }
50 return NULL;
51 }
52 buffer = new_buffer;
53
54 /* make the re-entrant call to get the grp structure */
55 errno = 0;
56 status = getgrnam_r(group, buffer,
57 sizeof(struct group) + (char *) buffer,
58 length, &result);
59 if (!status && (result == buffer)) {
60 char *data_name;
61 const void *ignore;
62 int i;
63
64 data_name = malloc(strlen("_pammodutil_getgrnam") + 1 +
65 strlen(group) + 1 + intlen(INT_MAX) + 1);
66 if ((pamh != NULL) && (data_name == NULL)) {
67 D(("was unable to register the data item [%s]",
68 pam_strerror(pamh, status)));
69 free(buffer);
70 return NULL;
71 }
72
73 if (pamh != NULL) {
74 for (i = 0; i < INT_MAX; i++) {
75 sprintf(data_name, "_pammodutil_getgrnam_%s_%d", group, i);
76 status = PAM_NO_MODULE_DATA;
77 if (pam_get_data(pamh, data_name, &ignore) != PAM_SUCCESS) {
78 status = pam_set_data(pamh, data_name,
79 result, pam_modutil_cleanup);
80 }
81 if (status == PAM_SUCCESS) {
82 break;
83 }
84 }
85 } else {
86 status = PAM_SUCCESS;
87 }
88
89 free(data_name);
90
91 if (status == PAM_SUCCESS) {
92 D(("success"));
93 return result;
94 }
95
96 D(("was unable to register the data item [%s]",
97 pam_strerror(pamh, status)));
98
99 free(buffer);
100 return NULL;
101
102 } else if (errno != ERANGE && errno != EINTR) {
103 /* no sense in repeating the call */
104 break;
105 }
106
107 length <<= PWD_LENGTH_SHIFT;
108
109 } while (length < PWD_ABSURD_PWD_LENGTH);
110
111 D(("grp structure took %u bytes or so of memory",
112 length+sizeof(struct group)));
113
114 free(buffer);
115 return NULL;
116
117 #else /* ie. ifndef HAVE_GETGRNAM_R */
118
119 /*
120 * Sorry, there does not appear to be a reentrant version of
121 * getgrnam(). So, we use the standard libc function.
122 */
123
124 return getgrnam(group);
125
126 #endif /* def HAVE_GETGRNAM_R */
127 }