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 : /* PASSWD 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 :
31 : struct sss_cli_mc_ctx pw_mc_ctx = { UNINITIALIZED, -1, 0, NULL, 0, NULL, 0,
32 : NULL, 0, 0 };
33 :
34 0 : static errno_t sss_nss_mc_parse_result(struct sss_mc_rec *rec,
35 : struct passwd *result,
36 : char *buffer, size_t buflen)
37 : {
38 : struct sss_mc_pwd_data *data;
39 : time_t expire;
40 : void *cookie;
41 : int ret;
42 :
43 : /* additional checks before filling result*/
44 0 : expire = rec->expire;
45 0 : if (expire < time(NULL)) {
46 : /* entry is now invalid */
47 0 : return EINVAL;
48 : }
49 :
50 0 : data = (struct sss_mc_pwd_data *)rec->data;
51 :
52 0 : if (data->strs_len > buflen) {
53 0 : return ERANGE;
54 : }
55 :
56 : /* fill in glibc provided structs */
57 :
58 : /* copy in buffer */
59 0 : memcpy(buffer, data->strs, data->strs_len);
60 :
61 : /* fill in passwd */
62 0 : result->pw_uid = data->uid;
63 0 : result->pw_gid = data->gid;
64 :
65 0 : cookie = NULL;
66 0 : ret = sss_nss_str_ptr_from_buffer(&result->pw_name, &cookie,
67 0 : buffer, data->strs_len);
68 0 : if (ret) {
69 0 : return ret;
70 : }
71 0 : ret = sss_nss_str_ptr_from_buffer(&result->pw_passwd, &cookie,
72 0 : buffer, data->strs_len);
73 0 : if (ret) {
74 0 : return ret;
75 : }
76 0 : ret = sss_nss_str_ptr_from_buffer(&result->pw_gecos, &cookie,
77 0 : buffer, data->strs_len);
78 0 : if (ret) {
79 0 : return ret;
80 : }
81 0 : ret = sss_nss_str_ptr_from_buffer(&result->pw_dir, &cookie,
82 0 : buffer, data->strs_len);
83 0 : if (ret) {
84 0 : return ret;
85 : }
86 0 : ret = sss_nss_str_ptr_from_buffer(&result->pw_shell, &cookie,
87 0 : buffer, data->strs_len);
88 0 : if (ret) {
89 0 : return ret;
90 : }
91 0 : if (cookie != NULL) {
92 0 : return EINVAL;
93 : }
94 :
95 0 : return 0;
96 : }
97 :
98 76 : errno_t sss_nss_mc_getpwnam(const char *name, size_t name_len,
99 : struct passwd *result,
100 : char *buffer, size_t buflen)
101 : {
102 76 : struct sss_mc_rec *rec = NULL;
103 : struct sss_mc_pwd_data *data;
104 : char *rec_name;
105 : uint32_t hash;
106 : uint32_t slot;
107 : int ret;
108 76 : const size_t strs_offset = offsetof(struct sss_mc_pwd_data, strs);
109 : size_t data_size;
110 :
111 76 : ret = sss_nss_mc_get_ctx("passwd", &pw_mc_ctx);
112 76 : if (ret) {
113 76 : return ret;
114 : }
115 :
116 : /* Get max size of data table. */
117 0 : data_size = pw_mc_ctx.dt_size;
118 :
119 : /* hashes are calculated including the NULL terminator */
120 0 : hash = sss_nss_mc_hash(&pw_mc_ctx, name, name_len + 1);
121 0 : slot = pw_mc_ctx.hash_table[hash];
122 :
123 : /* If slot is not within the bounds of mmaped region and
124 : * it's value is not MC_INVALID_VAL, then the cache is
125 : * probbably corrupted. */
126 0 : while (MC_SLOT_WITHIN_BOUNDS(slot, data_size)) {
127 : /* free record from previous iteration */
128 0 : free(rec);
129 0 : rec = NULL;
130 :
131 0 : ret = sss_nss_mc_get_record(&pw_mc_ctx, slot, &rec);
132 0 : if (ret) {
133 0 : goto done;
134 : }
135 :
136 : /* check record matches what we are searching for */
137 0 : if (hash != rec->hash1) {
138 : /* if name hash does not match we can skip this immediately */
139 0 : slot = sss_nss_mc_next_slot_with_hash(rec, hash);
140 0 : continue;
141 : }
142 :
143 0 : data = (struct sss_mc_pwd_data *)rec->data;
144 : /* Integrity check
145 : * - name_len cannot be longer than all strings
146 : * - data->name cannot point outside strings
147 : * - all strings must be within copy of record
148 : * - size of record must be lower that data table size */
149 0 : if (name_len > data->strs_len
150 0 : || (data->name + name_len) > (strs_offset + data->strs_len)
151 0 : || data->strs_len > rec->len
152 0 : || rec->len > data_size) {
153 0 : ret = ENOENT;
154 0 : goto done;
155 : }
156 :
157 0 : rec_name = (char *)data + data->name;
158 0 : if (strcmp(name, rec_name) == 0) {
159 0 : break;
160 : }
161 :
162 0 : slot = sss_nss_mc_next_slot_with_hash(rec, hash);
163 : }
164 :
165 0 : if (!MC_SLOT_WITHIN_BOUNDS(slot, data_size)) {
166 0 : ret = ENOENT;
167 0 : goto done;
168 : }
169 :
170 0 : ret = sss_nss_mc_parse_result(rec, result, buffer, buflen);
171 :
172 : done:
173 0 : free(rec);
174 0 : __sync_sub_and_fetch(&pw_mc_ctx.active_threads, 1);
175 0 : return ret;
176 : }
177 :
178 2 : errno_t sss_nss_mc_getpwuid(uid_t uid,
179 : struct passwd *result,
180 : char *buffer, size_t buflen)
181 : {
182 2 : struct sss_mc_rec *rec = NULL;
183 : struct sss_mc_pwd_data *data;
184 : char uidstr[11];
185 : uint32_t hash;
186 : uint32_t slot;
187 : int len;
188 : int ret;
189 :
190 2 : ret = sss_nss_mc_get_ctx("passwd", &pw_mc_ctx);
191 2 : if (ret) {
192 2 : return ret;
193 : }
194 :
195 0 : len = snprintf(uidstr, 11, "%ld", (long)uid);
196 0 : if (len > 10) {
197 0 : ret = EINVAL;
198 0 : goto done;
199 : }
200 :
201 : /* hashes are calculated including the NULL terminator */
202 0 : hash = sss_nss_mc_hash(&pw_mc_ctx, uidstr, len+1);
203 0 : slot = pw_mc_ctx.hash_table[hash];
204 :
205 : /* If slot is not within the bounds of mmaped region and
206 : * it's value is not MC_INVALID_VAL, then the cache is
207 : * probbably corrupted. */
208 0 : while (MC_SLOT_WITHIN_BOUNDS(slot, pw_mc_ctx.dt_size)) {
209 : /* free record from previous iteration */
210 0 : free(rec);
211 0 : rec = NULL;
212 :
213 0 : ret = sss_nss_mc_get_record(&pw_mc_ctx, slot, &rec);
214 0 : if (ret) {
215 0 : goto done;
216 : }
217 :
218 : /* check record matches what we are searching for */
219 0 : if (hash != rec->hash2) {
220 : /* if uid hash does not match we can skip this immediately */
221 0 : slot = sss_nss_mc_next_slot_with_hash(rec, hash);
222 0 : continue;
223 : }
224 :
225 0 : data = (struct sss_mc_pwd_data *)rec->data;
226 0 : if (uid == data->uid) {
227 0 : break;
228 : }
229 :
230 0 : slot = sss_nss_mc_next_slot_with_hash(rec, hash);
231 : }
232 :
233 0 : if (!MC_SLOT_WITHIN_BOUNDS(slot, pw_mc_ctx.dt_size)) {
234 0 : ret = ENOENT;
235 0 : goto done;
236 : }
237 :
238 0 : ret = sss_nss_mc_parse_result(rec, result, buffer, buflen);
239 :
240 : done:
241 0 : free(rec);
242 0 : __sync_sub_and_fetch(&pw_mc_ctx.active_threads, 1);
243 0 : return ret;
244 : }
245 :
|