Line data Source code
1 : /*
2 : SSSD
3 :
4 : Authors:
5 : Stephen Gallagher <sgallagh@redhat.com>
6 :
7 : Copyright (C) 2012 Red Hat
8 :
9 : This program is free software; you can redistribute it and/or modify
10 : it under the terms of the GNU General Public License as published by
11 : the Free Software Foundation; either version 3 of the License, or
12 : (at your option) any later version.
13 :
14 : This program is distributed in the hope that it will be useful,
15 : but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : GNU General Public License for more details.
18 :
19 : You should have received a copy of the GNU General Public License
20 : along with this program. If not, see <http://www.gnu.org/licenses/>.
21 : */
22 :
23 : #include "providers/proxy/proxy.h"
24 : #include "util/util.h"
25 : #include "util/strtonum.h"
26 : #include "db/sysdb_services.h"
27 :
28 : #define BUFLEN 1024
29 :
30 : errno_t
31 0 : proxy_save_service(struct sss_domain_info *domain,
32 : struct servent *svc,
33 : bool lowercase,
34 : uint64_t cache_timeout)
35 : {
36 : errno_t ret;
37 : char *cased_name;
38 : const char **protocols;
39 : const char **cased_aliases;
40 : TALLOC_CTX *tmp_ctx;
41 0 : char *lc_alias = NULL;
42 0 : time_t now = time(NULL);
43 :
44 0 : tmp_ctx = talloc_new(NULL);
45 0 : if (!tmp_ctx) return ENOMEM;
46 :
47 0 : cased_name = sss_get_cased_name(tmp_ctx, svc->s_name,
48 0 : domain->case_preserve);
49 0 : if (!cased_name) {
50 0 : ret = ENOMEM;
51 0 : goto done;
52 : }
53 :
54 0 : protocols = talloc_array(tmp_ctx, const char *, 2);
55 0 : if (!protocols) {
56 0 : ret = ENOMEM;
57 0 : goto done;
58 : }
59 :
60 0 : protocols[0] = sss_get_cased_name(protocols, svc->s_proto,
61 0 : !lowercase);
62 0 : if (!protocols[0]) {
63 0 : ret = ENOMEM;
64 0 : goto done;
65 : }
66 0 : protocols[1] = NULL;
67 :
68 : /* Count the aliases */
69 0 : ret = sss_get_cased_name_list(tmp_ctx,
70 0 : (const char * const *) svc->s_aliases,
71 0 : !lowercase, &cased_aliases);
72 0 : if (ret != EOK) {
73 0 : goto done;
74 : }
75 :
76 0 : if (domain->case_preserve) {
77 : /* Add lowercased alias to allow case-insensitive lookup */
78 0 : lc_alias = sss_tc_utf8_str_tolower(tmp_ctx, svc->s_name);
79 0 : if (lc_alias == NULL) {
80 0 : DEBUG(SSSDBG_OP_FAILURE, "Cannot convert name to lowercase.\n");
81 0 : ret = ENOMEM;
82 0 : goto done;
83 : }
84 :
85 0 : ret = add_string_to_list(tmp_ctx, lc_alias,
86 : discard_const_p(char **, &cased_aliases));
87 0 : if (ret != EOK) {
88 0 : DEBUG(SSSDBG_OP_FAILURE,
89 : "Failed to add lowercased name alias.\n");
90 0 : goto done;
91 : }
92 : }
93 :
94 0 : ret = sysdb_store_service(domain,
95 : cased_name,
96 0 : ntohs(svc->s_port),
97 : cased_aliases,
98 : protocols,
99 : NULL, NULL,
100 : cache_timeout,
101 : now);
102 : done:
103 0 : talloc_free(tmp_ctx);
104 0 : return ret;
105 : }
106 :
107 : errno_t
108 0 : get_serv_byname(struct proxy_id_ctx *ctx,
109 : struct sss_domain_info *dom,
110 : const char *name,
111 : const char *protocol)
112 : {
113 : errno_t ret;
114 : enum nss_status status;
115 : struct servent *result;
116 : TALLOC_CTX *tmp_ctx;
117 : char buffer[BUFLEN];
118 :
119 0 : tmp_ctx = talloc_new(NULL);
120 0 : if (!tmp_ctx) return ENOMEM;
121 :
122 0 : result = talloc_zero(tmp_ctx, struct servent);
123 0 : if (!result) {
124 0 : ret = ENOMEM;
125 0 : goto done;
126 : }
127 :
128 0 : status = ctx->ops.getservbyname_r(name, protocol, result,
129 : buffer, BUFLEN, &ret);
130 0 : if (status != NSS_STATUS_SUCCESS && status != NSS_STATUS_NOTFOUND) {
131 0 : DEBUG(SSSDBG_MINOR_FAILURE,
132 : "getservbyname_r failed for service [%s].\n", name);
133 0 : goto done;
134 : }
135 :
136 0 : if (status == NSS_STATUS_NOTFOUND) {
137 : /* Make sure we remove it from the cache */
138 0 : ret = sysdb_svc_delete(dom, name, 0, protocol);
139 : } else {
140 :
141 : /* Results found. Save them into the cache */
142 0 : ret = proxy_save_service(dom, result,
143 0 : !dom->case_sensitive,
144 0 : dom->service_timeout);
145 : }
146 :
147 : done:
148 0 : talloc_free(tmp_ctx);
149 0 : return ret;
150 : }
151 :
152 : errno_t
153 0 : get_serv_byport(struct proxy_id_ctx *ctx,
154 : struct sss_domain_info *dom,
155 : const char *be_filter,
156 : const char *protocol)
157 : {
158 : errno_t ret;
159 : enum nss_status status;
160 : struct servent *result;
161 : TALLOC_CTX *tmp_ctx;
162 : uint16_t port;
163 : char buffer[BUFLEN];
164 :
165 0 : tmp_ctx = talloc_new(NULL);
166 0 : if (!tmp_ctx) return ENOMEM;
167 :
168 0 : result = talloc_zero(tmp_ctx, struct servent);
169 0 : if (!result) {
170 0 : ret = ENOMEM;
171 0 : goto done;
172 : }
173 :
174 0 : errno = 0;
175 0 : port = htons(strtouint16(be_filter, NULL, 0));
176 0 : if (errno) {
177 0 : ret = errno;
178 0 : goto done;
179 : }
180 :
181 0 : status = ctx->ops.getservbyport_r(port, protocol, result,
182 : buffer, BUFLEN, &ret);
183 0 : if (status != NSS_STATUS_SUCCESS && status != NSS_STATUS_NOTFOUND) {
184 0 : DEBUG(SSSDBG_MINOR_FAILURE,
185 : "getservbyport_r failed for service [%s].\n", be_filter);
186 0 : goto done;
187 : }
188 :
189 0 : if (status == NSS_STATUS_NOTFOUND) {
190 : /* Make sure we remove it from the cache */
191 0 : ret = sysdb_svc_delete(dom, NULL, port, protocol);
192 : } else {
193 : /* Results found. Save them into the cache */
194 0 : ret = proxy_save_service(dom, result,
195 0 : !dom->case_sensitive,
196 0 : dom->service_timeout);
197 : }
198 :
199 : done:
200 0 : talloc_free(tmp_ctx);
201 0 : return ret;
202 : }
203 :
204 : errno_t
205 0 : enum_services(struct proxy_id_ctx *ctx,
206 : struct sysdb_ctx *sysdb,
207 : struct sss_domain_info *dom)
208 : {
209 : TALLOC_CTX *tmpctx;
210 0 : bool in_transaction = false;
211 : struct servent *svc;
212 : enum nss_status status;
213 : size_t buflen;
214 : char *buffer;
215 : char *newbuf;
216 : errno_t ret, sret;
217 0 : time_t now = time(NULL);
218 : const char **protocols;
219 : const char **cased_aliases;
220 : bool again;
221 :
222 0 : DEBUG(SSSDBG_TRACE_FUNC, "Enumerating services\n");
223 :
224 0 : tmpctx = talloc_new(NULL);
225 0 : if (!tmpctx) {
226 0 : return ENOMEM;
227 : }
228 :
229 0 : svc = talloc(tmpctx, struct servent);
230 0 : if (!svc) {
231 0 : ret = ENOMEM;
232 0 : goto done;
233 : }
234 :
235 0 : buflen = DEFAULT_BUFSIZE;
236 0 : buffer = talloc_size(tmpctx, buflen);
237 0 : if (!buffer) {
238 0 : ret = ENOMEM;
239 0 : goto done;
240 : }
241 :
242 0 : protocols = talloc_zero_array(tmpctx, const char *, 2);
243 0 : if (protocols == NULL) {
244 0 : ret = ENOMEM;
245 0 : goto done;
246 : }
247 :
248 0 : ret = sysdb_transaction_start(sysdb);
249 0 : if (ret) {
250 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to start transaction\n");
251 0 : goto done;
252 : }
253 0 : in_transaction = true;
254 :
255 0 : status = ctx->ops.setservent();
256 0 : if (status != NSS_STATUS_SUCCESS) {
257 0 : ret = EIO;
258 0 : goto done;
259 : }
260 :
261 : do {
262 0 : again = false;
263 :
264 : /* always zero out the svc structure */
265 0 : memset(svc, 0, sizeof(struct servent));
266 :
267 : /* get entry */
268 0 : status = ctx->ops.getservent_r(svc, buffer, buflen, &ret);
269 :
270 0 : switch (status) {
271 : case NSS_STATUS_TRYAGAIN:
272 : /* buffer too small ? */
273 0 : if (buflen < MAX_BUF_SIZE) {
274 0 : buflen *= 2;
275 : }
276 0 : if (buflen > MAX_BUF_SIZE) {
277 0 : buflen = MAX_BUF_SIZE;
278 : }
279 0 : newbuf = talloc_realloc_size(tmpctx, buffer, buflen);
280 0 : if (!newbuf) {
281 0 : ret = ENOMEM;
282 0 : goto done;
283 : }
284 0 : buffer = newbuf;
285 0 : again = true;
286 0 : break;
287 :
288 : case NSS_STATUS_NOTFOUND:
289 :
290 : /* we are done here */
291 0 : DEBUG(SSSDBG_TRACE_FUNC, "Enumeration completed.\n");
292 :
293 0 : ret = sysdb_transaction_commit(sysdb);
294 0 : if (ret != EOK) {
295 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to commit transaction\n");
296 0 : goto done;
297 : }
298 :
299 0 : in_transaction = false;
300 0 : break;
301 :
302 : case NSS_STATUS_SUCCESS:
303 :
304 0 : DEBUG(SSSDBG_TRACE_INTERNAL,
305 : "Service found (%s, %d/%s)\n",
306 : svc->s_name, svc->s_port, svc->s_proto);
307 :
308 0 : protocols[0] = sss_get_cased_name(protocols, svc->s_proto,
309 0 : dom->case_sensitive);
310 0 : if (!protocols[0]) {
311 0 : ret = ENOMEM;
312 0 : goto done;
313 : }
314 0 : protocols[1] = NULL;
315 :
316 0 : ret = sss_get_cased_name_list(tmpctx,
317 0 : (const char * const *) svc->s_aliases,
318 0 : dom->case_sensitive, &cased_aliases);
319 0 : if (ret != EOK) {
320 : /* Do not fail completely on errors.
321 : * Just report the failure to save and go on */
322 0 : DEBUG(SSSDBG_OP_FAILURE,
323 : "Failed to store service [%s]. Ignoring.\n",
324 : strerror(ret));
325 0 : again = true;
326 0 : break;
327 : }
328 :
329 0 : ret = sysdb_store_service(dom,
330 0 : svc->s_name,
331 : svc->s_port,
332 : cased_aliases,
333 : protocols,
334 : NULL, NULL,
335 0 : dom->service_timeout,
336 : now);
337 0 : if (ret) {
338 : /* Do not fail completely on errors.
339 : * Just report the failure to save and go on */
340 0 : DEBUG(SSSDBG_OP_FAILURE,
341 : "Failed to store service [%s]. Ignoring.\n",
342 : strerror(ret));
343 : }
344 0 : again = true;
345 0 : break;
346 :
347 : case NSS_STATUS_UNAVAIL:
348 : /* "remote" backend unavailable. Enter offline mode */
349 0 : ret = ENXIO;
350 0 : break;
351 :
352 : default:
353 0 : ret = EIO;
354 0 : DEBUG(SSSDBG_CRIT_FAILURE,
355 : "proxy -> getservent_r failed (%d)[%s]\n",
356 : ret, strerror(ret));
357 0 : break;
358 : }
359 0 : } while (again);
360 :
361 : done:
362 0 : talloc_zfree(tmpctx);
363 0 : if (in_transaction) {
364 0 : sret = sysdb_transaction_cancel(sysdb);
365 0 : if (sret != EOK) {
366 0 : DEBUG(SSSDBG_CRIT_FAILURE,
367 : "Could not cancel transaction! [%s]\n",
368 : strerror(sret));
369 : }
370 : }
371 0 : ctx->ops.endservent();
372 0 : return ret;
373 : }
|