Line data Source code
1 : /*
2 : * System Security Services Daemon. NSS client interface
3 : *
4 : * Copyright (C) Simo Sorce 2011
5 : *
6 : * This program is free software; you can redistribute it and/or modify
7 : * it under the terms of the GNU Lesser General Public License as
8 : * published by the Free Software Foundation; either version 2.1 of the
9 : * License, or (at your option) any later version.
10 : *
11 : * This program is distributed in the hope that it will be useful,
12 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : * GNU Lesser General Public License for more details.
15 : *
16 : * You should have received a copy of the GNU Lesser General Public License
17 : * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 : */
19 :
20 : /* GROUP database NSS interface using mmap cache */
21 :
22 : #include <errno.h>
23 : #include <stdio.h>
24 : #include <string.h>
25 : #include <stdlib.h>
26 : #include <stddef.h>
27 : #include <sys/mman.h>
28 : #include <time.h>
29 : #include "nss_mc.h"
30 : #include "util/util_safealign.h"
31 :
32 : struct sss_cli_mc_ctx gr_mc_ctx = { UNINITIALIZED, -1, 0, NULL, 0, NULL, 0,
33 : NULL, 0, 0 };
34 :
35 0 : static errno_t sss_nss_mc_parse_result(struct sss_mc_rec *rec,
36 : struct group *result,
37 : char *buffer, size_t buflen)
38 : {
39 : struct sss_mc_grp_data *data;
40 : time_t expire;
41 : void *cookie;
42 : char *membuf;
43 : size_t memsize;
44 : int ret;
45 : int i;
46 :
47 : /* additional checks before filling result*/
48 0 : expire = rec->expire;
49 0 : if (expire < time(NULL)) {
50 : /* entry is now invalid */
51 0 : return EINVAL;
52 : }
53 :
54 0 : data = (struct sss_mc_grp_data *)rec->data;
55 :
56 0 : memsize = (data->members + 1) * sizeof(char *);
57 0 : if (data->strs_len + memsize > buflen) {
58 0 : return ERANGE;
59 : }
60 :
61 : /* fill in glibc provided structs */
62 :
63 : /* copy in buffer */
64 0 : membuf = buffer + memsize;
65 0 : memcpy(membuf, data->strs, data->strs_len);
66 :
67 : /* fill in group */
68 0 : result->gr_gid = data->gid;
69 :
70 : /* The address &buffer[0] must be aligned to sizeof(char *) */
71 0 : if (!IS_ALIGNED(buffer, char *)) {
72 : /* The buffer is not properly aligned. */
73 0 : return EFAULT;
74 : }
75 :
76 0 : result->gr_mem = DISCARD_ALIGN(buffer, char **);
77 0 : result->gr_mem[data->members] = NULL;
78 :
79 0 : cookie = NULL;
80 0 : ret = sss_nss_str_ptr_from_buffer(&result->gr_name, &cookie,
81 0 : membuf, data->strs_len);
82 0 : if (ret) {
83 0 : return ret;
84 : }
85 0 : ret = sss_nss_str_ptr_from_buffer(&result->gr_passwd, &cookie,
86 0 : membuf, data->strs_len);
87 0 : if (ret) {
88 0 : return ret;
89 : }
90 :
91 0 : for (i = 0; i < data->members; i++) {
92 0 : ret = sss_nss_str_ptr_from_buffer(&result->gr_mem[i], &cookie,
93 0 : membuf, data->strs_len);
94 0 : if (ret) {
95 0 : return ret;
96 : }
97 : }
98 0 : if (cookie != NULL) {
99 0 : return EINVAL;
100 : }
101 :
102 0 : return 0;
103 : }
104 :
105 48 : errno_t sss_nss_mc_getgrnam(const char *name, size_t name_len,
106 : struct group *result,
107 : char *buffer, size_t buflen)
108 : {
109 48 : struct sss_mc_rec *rec = NULL;
110 : struct sss_mc_grp_data *data;
111 : char *rec_name;
112 : uint32_t hash;
113 : uint32_t slot;
114 : int ret;
115 48 : const size_t strs_offset = offsetof(struct sss_mc_grp_data, strs);
116 : size_t data_size;
117 :
118 48 : ret = sss_nss_mc_get_ctx("group", &gr_mc_ctx);
119 48 : if (ret) {
120 48 : return ret;
121 : }
122 :
123 : /* Get max size of data table. */
124 0 : data_size = gr_mc_ctx.dt_size;
125 :
126 : /* hashes are calculated including the NULL terminator */
127 0 : hash = sss_nss_mc_hash(&gr_mc_ctx, name, name_len + 1);
128 0 : slot = gr_mc_ctx.hash_table[hash];
129 :
130 : /* If slot is not within the bounds of mmaped region and
131 : * it's value is not MC_INVALID_VAL, then the cache is
132 : * probbably corrupted. */
133 0 : while (MC_SLOT_WITHIN_BOUNDS(slot, data_size)) {
134 : /* free record from previous iteration */
135 0 : free(rec);
136 0 : rec = NULL;
137 :
138 0 : ret = sss_nss_mc_get_record(&gr_mc_ctx, slot, &rec);
139 0 : if (ret) {
140 0 : goto done;
141 : }
142 :
143 : /* check record matches what we are searching for */
144 0 : if (hash != rec->hash1) {
145 : /* if name hash does not match we can skip this immediately */
146 0 : slot = sss_nss_mc_next_slot_with_hash(rec, hash);
147 0 : continue;
148 : }
149 :
150 0 : data = (struct sss_mc_grp_data *)rec->data;
151 : /* Integrity check
152 : * - name_len cannot be longer than all strings
153 : * - data->name cannot point outside strings
154 : * - all strings must be within copy of record
155 : * - size of record must be lower that data table size */
156 0 : if (name_len > data->strs_len
157 0 : || (data->name + name_len) > (strs_offset + data->strs_len)
158 0 : || data->strs_len > rec->len
159 0 : || rec->len > data_size) {
160 0 : ret = ENOENT;
161 0 : goto done;
162 : }
163 :
164 0 : rec_name = (char *)data + data->name;
165 0 : if (strcmp(name, rec_name) == 0) {
166 0 : break;
167 : }
168 :
169 0 : slot = sss_nss_mc_next_slot_with_hash(rec, hash);
170 : }
171 :
172 0 : if (!MC_SLOT_WITHIN_BOUNDS(slot, data_size)) {
173 0 : ret = ENOENT;
174 0 : goto done;
175 : }
176 :
177 0 : ret = sss_nss_mc_parse_result(rec, result, buffer, buflen);
178 :
179 : done:
180 0 : free(rec);
181 0 : __sync_sub_and_fetch(&gr_mc_ctx.active_threads, 1);
182 0 : return ret;
183 : }
184 :
185 4 : errno_t sss_nss_mc_getgrgid(gid_t gid,
186 : struct group *result,
187 : char *buffer, size_t buflen)
188 : {
189 4 : struct sss_mc_rec *rec = NULL;
190 : struct sss_mc_grp_data *data;
191 : char gidstr[11];
192 : uint32_t hash;
193 : uint32_t slot;
194 : int len;
195 : int ret;
196 :
197 4 : ret = sss_nss_mc_get_ctx("group", &gr_mc_ctx);
198 4 : if (ret) {
199 4 : return ret;
200 : }
201 :
202 0 : len = snprintf(gidstr, 11, "%ld", (long)gid);
203 0 : if (len > 10) {
204 0 : ret = EINVAL;
205 0 : goto done;
206 : }
207 :
208 : /* hashes are calculated including the NULL terminator */
209 0 : hash = sss_nss_mc_hash(&gr_mc_ctx, gidstr, len+1);
210 0 : slot = gr_mc_ctx.hash_table[hash];
211 :
212 : /* If slot is not within the bounds of mmaped region and
213 : * it's value is not MC_INVALID_VAL, then the cache is
214 : * probbably corrupted. */
215 0 : while (MC_SLOT_WITHIN_BOUNDS(slot, gr_mc_ctx.dt_size)) {
216 : /* free record from previous iteration */
217 0 : free(rec);
218 0 : rec = NULL;
219 :
220 0 : ret = sss_nss_mc_get_record(&gr_mc_ctx, slot, &rec);
221 0 : if (ret) {
222 0 : goto done;
223 : }
224 :
225 : /* check record matches what we are searching for */
226 0 : if (hash != rec->hash2) {
227 : /* if uid hash does not match we can skip this immediately */
228 0 : slot = sss_nss_mc_next_slot_with_hash(rec, hash);
229 0 : continue;
230 : }
231 :
232 0 : data = (struct sss_mc_grp_data *)rec->data;
233 0 : if (gid == data->gid) {
234 0 : break;
235 : }
236 :
237 0 : slot = sss_nss_mc_next_slot_with_hash(rec, hash);
238 : }
239 :
240 0 : if (!MC_SLOT_WITHIN_BOUNDS(slot, gr_mc_ctx.dt_size)) {
241 0 : ret = ENOENT;
242 0 : goto done;
243 : }
244 :
245 0 : ret = sss_nss_mc_parse_result(rec, result, buffer, buflen);
246 :
247 : done:
248 0 : free(rec);
249 0 : __sync_sub_and_fetch(&gr_mc_ctx.active_threads, 1);
250 0 : return ret;
251 : }
252 :
|