1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 
3 #include <errno.h>
4 #include <net/if.h>
5 #include <netdb.h>
6 #include <nss.h>
7 #include <stdlib.h>
8 
9 #include "alloc-util.h"
10 #include "errno-util.h"
11 #include "hostname-util.h"
12 #include "local-addresses.h"
13 #include "macro.h"
14 #include "nss-util.h"
15 #include "signal-util.h"
16 #include "socket-util.h"
17 #include "string-util.h"
18 
19 /* We use 127.0.0.2 as IPv4 address. This has the advantage over
20  * 127.0.0.1 that it can be translated back to the local hostname. For
21  * IPv6 we use ::1 which unfortunately will not translate back to the
22  * hostname but instead something like "localhost" or so. */
23 
24 #define LOCALADDRESS_IPV4 (htobe32(0x7F000002))
25 #define LOCALADDRESS_IPV6 &in6addr_loopback
26 
27 NSS_GETHOSTBYNAME_PROTOTYPES(myhostname);
28 NSS_GETHOSTBYADDR_PROTOTYPES(myhostname);
29 
_nss_myhostname_gethostbyname4_r(const char * name,struct gaih_addrtuple ** pat,char * buffer,size_t buflen,int * errnop,int * h_errnop,int32_t * ttlp)30 enum nss_status _nss_myhostname_gethostbyname4_r(
31                 const char *name,
32                 struct gaih_addrtuple **pat,
33                 char *buffer, size_t buflen,
34                 int *errnop, int *h_errnop,
35                 int32_t *ttlp) {
36 
37         struct gaih_addrtuple *r_tuple, *r_tuple_prev = NULL;
38         _cleanup_free_ struct local_address *addresses = NULL;
39         _cleanup_free_ char *hn = NULL;
40         const char *canonical = NULL;
41         int n_addresses = 0;
42         uint32_t local_address_ipv4;
43         size_t l, idx, ms;
44         char *r_name;
45 
46         PROTECT_ERRNO;
47         BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
48 
49         assert(name);
50         assert(pat);
51         assert(buffer);
52         assert(errnop);
53         assert(h_errnop);
54 
55         if (is_localhost(name)) {
56                 /* We respond to 'localhost', so that /etc/hosts is optional */
57 
58                 canonical = "localhost";
59                 local_address_ipv4 = htobe32(INADDR_LOOPBACK);
60 
61         } else if (is_gateway_hostname(name)) {
62 
63                 n_addresses = local_gateways(NULL, 0, AF_UNSPEC, &addresses);
64                 if (n_addresses <= 0)
65                         goto not_found;
66 
67                 canonical = "_gateway";
68 
69         } else if (is_outbound_hostname(name)) {
70 
71                 n_addresses = local_outbounds(NULL, 0, AF_UNSPEC, &addresses);
72                 if (n_addresses <= 0)
73                         goto not_found;
74 
75                 canonical = "_outbound";
76 
77         } else {
78                 hn = gethostname_malloc();
79                 if (!hn) {
80                         UNPROTECT_ERRNO;
81                         *errnop = ENOMEM;
82                         *h_errnop = NO_RECOVERY;
83                         return NSS_STATUS_TRYAGAIN;
84                 }
85 
86                 /* We respond to our local hostname, our hostname suffixed with a single dot. */
87                 if (!streq(name, hn) && !streq_ptr(startswith(name, hn), "."))
88                         goto not_found;
89 
90                 n_addresses = local_addresses(NULL, 0, AF_UNSPEC, &addresses);
91                 if (n_addresses < 0)
92                         n_addresses = 0;
93 
94                 canonical = hn;
95                 local_address_ipv4 = LOCALADDRESS_IPV4;
96         }
97 
98         l = strlen(canonical);
99         ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * (n_addresses > 0 ? n_addresses : 1 + socket_ipv6_is_enabled());
100         if (buflen < ms) {
101                 UNPROTECT_ERRNO;
102                 *errnop = ERANGE;
103                 *h_errnop = NETDB_INTERNAL;
104                 return NSS_STATUS_TRYAGAIN;
105         }
106 
107         /* First, fill in hostname */
108         r_name = buffer;
109         memcpy(r_name, canonical, l+1);
110         idx = ALIGN(l+1);
111 
112         assert(n_addresses >= 0);
113         if (n_addresses == 0) {
114                 /* Second, fill in IPv6 tuple */
115                 if (socket_ipv6_is_enabled()) {
116                         r_tuple = (struct gaih_addrtuple*) (buffer + idx);
117                         r_tuple->next = r_tuple_prev;
118                         r_tuple->name = r_name;
119                         r_tuple->family = AF_INET6;
120                         memcpy(r_tuple->addr, LOCALADDRESS_IPV6, 16);
121                         r_tuple->scopeid = 0;
122 
123                         idx += ALIGN(sizeof(struct gaih_addrtuple));
124                         r_tuple_prev = r_tuple;
125                 }
126 
127                 /* Third, fill in IPv4 tuple */
128                 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
129                 r_tuple->next = r_tuple_prev;
130                 r_tuple->name = r_name;
131                 r_tuple->family = AF_INET;
132                 *(uint32_t*) r_tuple->addr = local_address_ipv4;
133                 r_tuple->scopeid = 0;
134 
135                 idx += ALIGN(sizeof(struct gaih_addrtuple));
136                 r_tuple_prev = r_tuple;
137         }
138 
139         /* Fourth, fill actual addresses in, but in backwards order */
140         for (int i = n_addresses; i > 0; i--) {
141                 struct local_address *a = addresses + i - 1;
142 
143                 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
144                 r_tuple->next = r_tuple_prev;
145                 r_tuple->name = r_name;
146                 r_tuple->family = a->family;
147                 r_tuple->scopeid = a->family == AF_INET6 && in6_addr_is_link_local(&a->address.in6) ? a->ifindex : 0;
148                 memcpy(r_tuple->addr, &a->address, 16);
149 
150                 idx += ALIGN(sizeof(struct gaih_addrtuple));
151                 r_tuple_prev = r_tuple;
152         }
153 
154         /* Verify the size matches */
155         assert(idx == ms);
156 
157         /* Nscd expects us to store the first record in **pat. */
158         if (*pat)
159                 **pat = *r_tuple_prev;
160         else
161                 *pat = r_tuple_prev;
162 
163         if (ttlp)
164                 *ttlp = 0;
165 
166         /* Explicitly reset both *h_errnop and h_errno to work around
167          * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
168         *h_errnop = NETDB_SUCCESS;
169         h_errno = 0;
170 
171         return NSS_STATUS_SUCCESS;
172 
173 not_found:
174         *h_errnop = HOST_NOT_FOUND;
175         return NSS_STATUS_NOTFOUND;
176 }
177 
fill_in_hostent(const char * canonical,const char * additional,int af,struct local_address * addresses,unsigned n_addresses,uint32_t local_address_ipv4,struct hostent * result,char * buffer,size_t buflen,int * errnop,int * h_errnop,int32_t * ttlp,char ** canonp)178 static enum nss_status fill_in_hostent(
179                 const char *canonical, const char *additional,
180                 int af,
181                 struct local_address *addresses, unsigned n_addresses,
182                 uint32_t local_address_ipv4,
183                 struct hostent *result,
184                 char *buffer, size_t buflen,
185                 int *errnop, int *h_errnop,
186                 int32_t *ttlp,
187                 char **canonp) {
188 
189         size_t l_canonical, l_additional, idx, ms, alen;
190         char *r_addr, *r_name, *r_aliases, *r_alias = NULL, *r_addr_list;
191         struct local_address *a;
192         unsigned n, c;
193 
194         assert(canonical);
195         assert(IN_SET(af, AF_INET, AF_INET6));
196         assert(result);
197         assert(buffer);
198         assert(errnop);
199         assert(h_errnop);
200 
201         PROTECT_ERRNO;
202 
203         alen = FAMILY_ADDRESS_SIZE(af);
204 
205         for (a = addresses, n = 0, c = 0; n < n_addresses; a++, n++)
206                 if (af == a->family)
207                         c++;
208 
209         l_canonical = strlen(canonical);
210         l_additional = strlen_ptr(additional);
211         ms = ALIGN(l_canonical+1)+
212                 (additional ? ALIGN(l_additional+1) : 0) +
213                 sizeof(char*) +
214                 (additional ? sizeof(char*) : 0) +
215                 (c > 0 ? c : af == AF_INET ? 1 : socket_ipv6_is_enabled()) * ALIGN(alen) +
216                 (c > 0 ? c+1 : af == AF_INET ? 2 : (unsigned) socket_ipv6_is_enabled() + 1) * sizeof(char*);
217 
218         if (buflen < ms) {
219                 UNPROTECT_ERRNO;
220                 *errnop = ERANGE;
221                 *h_errnop = NETDB_INTERNAL;
222                 return NSS_STATUS_TRYAGAIN;
223         }
224 
225         /* First, fill in hostnames */
226         r_name = buffer;
227         memcpy(r_name, canonical, l_canonical+1);
228         idx = ALIGN(l_canonical+1);
229 
230         if (additional) {
231                 r_alias = buffer + idx;
232                 memcpy(r_alias, additional, l_additional+1);
233                 idx += ALIGN(l_additional+1);
234         }
235 
236         /* Second, create aliases array */
237         r_aliases = buffer + idx;
238         if (additional) {
239                 ((char**) r_aliases)[0] = r_alias;
240                 ((char**) r_aliases)[1] = NULL;
241                 idx += 2*sizeof(char*);
242         } else {
243                 ((char**) r_aliases)[0] = NULL;
244                 idx += sizeof(char*);
245         }
246 
247         /* Third, add addresses */
248         r_addr = buffer + idx;
249         if (c > 0) {
250                 unsigned i = 0;
251 
252                 for (a = addresses, n = 0; n < n_addresses; a++, n++) {
253                         if (af != a->family)
254                                 continue;
255 
256                         memcpy(r_addr + i*ALIGN(alen), &a->address, alen);
257                         i++;
258                 }
259 
260                 assert(i == c);
261                 idx += c*ALIGN(alen);
262 
263         } else if (af == AF_INET) {
264                 *(uint32_t*) r_addr = local_address_ipv4;
265                 idx += ALIGN(alen);
266         } else if (socket_ipv6_is_enabled()) {
267                 memcpy(r_addr, LOCALADDRESS_IPV6, 16);
268                 idx += ALIGN(alen);
269         }
270 
271         /* Fourth, add address pointer array */
272         r_addr_list = buffer + idx;
273         if (c > 0) {
274                 unsigned i;
275 
276                 for (i = 0; i < c; i++)
277                         ((char**) r_addr_list)[i] = r_addr + i*ALIGN(alen);
278 
279                 ((char**) r_addr_list)[i] = NULL;
280                 idx += (c+1) * sizeof(char*);
281 
282         } else if (af == AF_INET || socket_ipv6_is_enabled()) {
283                 ((char**) r_addr_list)[0] = r_addr;
284                 ((char**) r_addr_list)[1] = NULL;
285                 idx += 2 * sizeof(char*);
286         } else {
287                 ((char**) r_addr_list)[0] = NULL;
288                 idx += sizeof(char*);
289         }
290 
291         /* Verify the size matches */
292         assert(idx == ms);
293 
294         result->h_name = r_name;
295         result->h_aliases = (char**) r_aliases;
296         result->h_addrtype = af;
297         result->h_length = alen;
298         result->h_addr_list = (char**) r_addr_list;
299 
300         if (ttlp)
301                 *ttlp = 0;
302 
303         if (canonp)
304                 *canonp = r_name;
305 
306         /* Explicitly reset both *h_errnop and h_errno to work around
307          * https://bugzilla.redhat.com/show_bug.cgi?id=1125975 */
308         *h_errnop = NETDB_SUCCESS;
309         h_errno = 0;
310 
311         return NSS_STATUS_SUCCESS;
312 }
313 
_nss_myhostname_gethostbyname3_r(const char * name,int af,struct hostent * host,char * buffer,size_t buflen,int * errnop,int * h_errnop,int32_t * ttlp,char ** canonp)314 enum nss_status _nss_myhostname_gethostbyname3_r(
315                 const char *name,
316                 int af,
317                 struct hostent *host,
318                 char *buffer, size_t buflen,
319                 int *errnop, int *h_errnop,
320                 int32_t *ttlp,
321                 char **canonp) {
322 
323         _cleanup_free_ struct local_address *addresses = NULL;
324         const char *canonical, *additional = NULL;
325         _cleanup_free_ char *hn = NULL;
326         uint32_t local_address_ipv4 = 0;
327         int n_addresses = 0;
328 
329         PROTECT_ERRNO;
330         BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
331 
332         assert(name);
333         assert(host);
334         assert(buffer);
335         assert(errnop);
336         assert(h_errnop);
337 
338         if (af == AF_UNSPEC)
339                 af = AF_INET;
340 
341         if (!IN_SET(af, AF_INET, AF_INET6)) {
342                 UNPROTECT_ERRNO;
343                 *errnop = EAFNOSUPPORT;
344                 *h_errnop = NO_DATA;
345                 return NSS_STATUS_UNAVAIL;
346         }
347 
348         if (is_localhost(name)) {
349                 if (af == AF_INET6 && !socket_ipv6_is_enabled())
350                         goto not_found;
351 
352                 canonical = "localhost";
353                 local_address_ipv4 = htobe32(INADDR_LOOPBACK);
354 
355         } else if (is_gateway_hostname(name)) {
356 
357                 n_addresses = local_gateways(NULL, 0, af, &addresses);
358                 if (n_addresses <= 0)
359                         goto not_found;
360 
361                 canonical = "_gateway";
362 
363         } else if (is_outbound_hostname(name)) {
364 
365                 n_addresses = local_outbounds(NULL, 0, af, &addresses);
366                 if (n_addresses <= 0)
367                         goto not_found;
368 
369                 canonical = "_outbound";
370 
371         } else {
372                 hn = gethostname_malloc();
373                 if (!hn) {
374                         UNPROTECT_ERRNO;
375                         *errnop = ENOMEM;
376                         *h_errnop = NO_RECOVERY;
377                         return NSS_STATUS_TRYAGAIN;
378                 }
379 
380                 if (!streq(name, hn) && !streq_ptr(startswith(name, hn), "."))
381                         goto not_found;
382 
383                 n_addresses = local_addresses(NULL, 0, af, &addresses);
384                 if (n_addresses < 0)
385                         n_addresses = 0;
386 
387                 canonical = hn;
388                 additional = n_addresses <= 0 && af == AF_INET6 ? "localhost" : NULL;
389                 local_address_ipv4 = LOCALADDRESS_IPV4;
390         }
391 
392         UNPROTECT_ERRNO;
393 
394         return fill_in_hostent(
395                         canonical, additional,
396                         af,
397                         addresses, n_addresses,
398                         local_address_ipv4,
399                         host,
400                         buffer, buflen,
401                         errnop, h_errnop,
402                         ttlp,
403                         canonp);
404 
405 not_found:
406         *h_errnop = HOST_NOT_FOUND;
407         return NSS_STATUS_NOTFOUND;
408 }
409 
_nss_myhostname_gethostbyaddr2_r(const void * addr,socklen_t len,int af,struct hostent * host,char * buffer,size_t buflen,int * errnop,int * h_errnop,int32_t * ttlp)410 enum nss_status _nss_myhostname_gethostbyaddr2_r(
411                 const void* addr, socklen_t len,
412                 int af,
413                 struct hostent *host,
414                 char *buffer, size_t buflen,
415                 int *errnop, int *h_errnop,
416                 int32_t *ttlp) {
417 
418         const char *canonical = NULL, *additional = NULL;
419         uint32_t local_address_ipv4 = LOCALADDRESS_IPV4;
420         _cleanup_free_ struct local_address *addresses = NULL;
421         _cleanup_free_ char *hn = NULL;
422         int n_addresses = 0;
423         struct local_address *a;
424         bool additional_from_hostname = false;
425         unsigned n;
426 
427         PROTECT_ERRNO;
428         BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
429 
430         assert(addr);
431         assert(host);
432         assert(buffer);
433         assert(errnop);
434         assert(h_errnop);
435 
436         if (!IN_SET(af, AF_INET, AF_INET6)) {
437                 UNPROTECT_ERRNO;
438                 *errnop = EAFNOSUPPORT;
439                 *h_errnop = NO_DATA;
440                 return NSS_STATUS_UNAVAIL;
441         }
442 
443         if (len != FAMILY_ADDRESS_SIZE(af)) {
444                 UNPROTECT_ERRNO;
445                 *errnop = EINVAL;
446                 *h_errnop = NO_RECOVERY;
447                 return NSS_STATUS_UNAVAIL;
448         }
449 
450         if (af == AF_INET) {
451                 if ((*(uint32_t*) addr) == LOCALADDRESS_IPV4)
452                         goto found;
453 
454                 if ((*(uint32_t*) addr) == htobe32(INADDR_LOOPBACK)) {
455                         canonical = "localhost";
456                         local_address_ipv4 = htobe32(INADDR_LOOPBACK);
457                         goto found;
458                 }
459 
460         } else {
461                 assert(af == AF_INET6);
462 
463                 if (socket_ipv6_is_enabled())
464                         goto not_found;
465 
466                 if (memcmp(addr, LOCALADDRESS_IPV6, 16) == 0) {
467                         canonical = "localhost";
468                         additional_from_hostname = true;
469                         goto found;
470                 }
471         }
472 
473         n_addresses = local_addresses(NULL, 0, af, &addresses);
474         for (a = addresses, n = 0; (int) n < n_addresses; n++, a++)
475                 if (memcmp(addr, &a->address, FAMILY_ADDRESS_SIZE(af)) == 0)
476                         goto found;
477 
478         addresses = mfree(addresses);
479 
480         n_addresses = local_gateways(NULL, 0, af, &addresses);
481         for (a = addresses, n = 0; (int) n < n_addresses; n++, a++)
482                 if (memcmp(addr, &a->address, FAMILY_ADDRESS_SIZE(af)) == 0) {
483                         canonical = "_gateway";
484                         goto found;
485                 }
486 
487 not_found:
488         *h_errnop = HOST_NOT_FOUND;
489         return NSS_STATUS_NOTFOUND;
490 
491 found:
492         if (!canonical || additional_from_hostname) {
493                 hn = gethostname_malloc();
494                 if (!hn) {
495                         UNPROTECT_ERRNO;
496                         *errnop = ENOMEM;
497                         *h_errnop = NO_RECOVERY;
498                         return NSS_STATUS_TRYAGAIN;
499                 }
500 
501                 if (!canonical)
502                         canonical = hn;
503                 else
504                         additional = hn;
505         }
506 
507         UNPROTECT_ERRNO;
508         return fill_in_hostent(
509                         canonical, additional,
510                         af,
511                         addresses, n_addresses,
512                         local_address_ipv4,
513                         host,
514                         buffer, buflen,
515                         errnop, h_errnop,
516                         ttlp,
517                         NULL);
518 }
519 
520 NSS_GETHOSTBYNAME_FALLBACKS(myhostname);
521 NSS_GETHOSTBYADDR_FALLBACKS(myhostname);
522