1 /*
2  * Host-Based-Access-control file support.
3  *
4  * Copyright (c) 2015 Marko Kreen
5  *
6  * Permission to use, copy, modify, and/or distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include "bouncer.h"
20 
21 #include <usual/cxextra.h>
22 #include <usual/cbtree.h>
23 #include <usual/fileutil.h>
24 
25 enum RuleType {
26 	RULE_LOCAL,
27 	RULE_HOST,
28 	RULE_HOSTSSL,
29 	RULE_HOSTNOSSL,
30 };
31 
32 #define NAME_ALL	1
33 #define NAME_SAMEUSER	2
34 
35 struct NameSlot {
36 	size_t strlen;
37 	char str[];
38 };
39 
40 struct HBAName {
41 	unsigned int flags;
42 	struct StrSet *name_set;
43 };
44 
45 struct HBARule {
46 	struct List node;
47 	enum RuleType rule_type;
48 	int rule_method;
49 	int rule_af;
50 	uint8_t rule_addr[16];
51 	uint8_t rule_mask[16];
52 	struct HBAName db_name;
53 	struct HBAName user_name;
54 };
55 
56 struct HBA {
57 	struct List rules;
58 };
59 
60 /*
61  * StrSet
62  */
63 
64 struct StrSetNode {
65 	unsigned int s_len;
66 	char s_val[FLEX_ARRAY];
67 };
68 
69 struct StrSet {
70 	CxMem *pool;
71 	unsigned count;
72 	unsigned alloc;
73 	struct StrSetNode **nodes;
74 	struct CBTree *cbtree;
75 };
76 
77 struct StrSet *strset_new(CxMem *cx);
78 void strset_free(struct StrSet *set);
79 bool strset_add(struct StrSet *set, const char *str, unsigned int len);
80 bool strset_contains(struct StrSet *set, const char *str, unsigned int len);
81 
strset_new(CxMem * cx)82 struct StrSet *strset_new(CxMem *cx)
83 {
84 	struct StrSet *set;
85 	CxMem *pool;
86 
87 	pool = cx_new_pool(cx, 1024, 0);
88 	if (!pool)
89 		return NULL;
90 	set = cx_alloc(pool, sizeof *set);
91 	if (!set)
92 		return NULL;
93 	set->pool = pool;
94 	set->cbtree = NULL;
95 	set->count = 0;
96 	set->alloc = 10;
97 	set->nodes = cx_alloc0(pool, set->alloc * sizeof(struct StrSet *));
98 	if (!set->nodes) {
99 		cx_destroy(pool);
100 		return NULL;
101 	}
102 	return set;
103 }
104 
strset_node_key(void * ctx,void * obj,const void ** ptr_p)105 static size_t strset_node_key(void *ctx, void *obj, const void **ptr_p)
106 {
107 	struct StrSetNode *node = obj;
108 	*ptr_p = node->s_val;
109 	return node->s_len;
110 }
111 
strset_add(struct StrSet * set,const char * str,unsigned int len)112 bool strset_add(struct StrSet *set, const char *str, unsigned int len)
113 {
114 	struct StrSetNode *node;
115 	unsigned int i;
116 	bool ok;
117 
118 	if (strset_contains(set, str, len))
119 		return true;
120 
121 	node = cx_alloc(set->pool, offsetof(struct StrSetNode, s_val) + len + 1);
122 	if (!node)
123 		return false;
124 	node->s_len = len;
125 	memcpy(node->s_val, str, len);
126 	node->s_val[len] = 0;
127 
128 	if (set->count < set->alloc) {
129 		set->nodes[set->count++] = node;
130 		return true;
131 	}
132 
133 	if (!set->cbtree) {
134 		set->cbtree = cbtree_create(strset_node_key, NULL, set, set->pool);
135 		if (!set->cbtree)
136 			return false;
137 		for (i = 0; i < set->count; i++) {
138 			ok = cbtree_insert(set->cbtree, set->nodes[i]);
139 			if (!ok)
140 				return false;
141 		}
142 	}
143 	ok = cbtree_insert(set->cbtree, node);
144 	if (!ok)
145 		return false;
146 	set->count++;
147 	return true;
148 }
149 
strset_contains(struct StrSet * set,const char * str,unsigned int len)150 bool strset_contains(struct StrSet *set, const char *str, unsigned int len)
151 {
152 	unsigned int i;
153 	struct StrSetNode *node;
154 	if (set->cbtree)
155 		return cbtree_lookup(set->cbtree, str, len) != NULL;
156 	for (i = 0; i < set->count; i++) {
157 		node = set->nodes[i];
158 		if (node->s_len != len)
159 			continue;
160 		if (memcmp(node->s_val, str, len) == 0)
161 			return true;
162 	}
163 	return false;
164 }
165 
strset_free(struct StrSet * set)166 void strset_free(struct StrSet *set)
167 {
168 	if (set)
169 		cx_destroy(set->pool);
170 }
171 
172 /*
173  * Parse HBA tokens.
174  */
175 
176 enum TokType {
177 	TOK_STRING,
178 	TOK_IDENT,
179 	TOK_COMMA,
180 	TOK_FAIL,
181 	TOK_EOL
182 };
183 
184 struct TokParser {
185 	const char *pos;
186 	enum TokType cur_tok;
187 	char *cur_tok_str;
188 
189 	char *buf;
190 	size_t buflen;
191 };
192 
tok_buf_check(struct TokParser * p,size_t len)193 static bool tok_buf_check(struct TokParser *p, size_t len)
194 {
195 	size_t tmplen;
196 	char *tmp;
197 	if (p->buflen >= len)
198 		return true;
199 	tmplen = len*2;
200 	tmp = realloc(p->buf, tmplen);
201 	if (!tmp)
202 		return false;
203 	p->buf = tmp;
204 	p->buflen = tmplen;
205 	return true;
206 }
207 
next_token(struct TokParser * p)208 static enum TokType next_token(struct TokParser *p)
209 {
210 	const char *s, *s2;
211 	char *dst;
212 	if (p->cur_tok == TOK_EOL)
213 		return TOK_EOL;
214 	p->cur_tok_str = NULL;
215 	p->cur_tok = TOK_FAIL;
216 
217 	while (p->pos[0] && isspace((unsigned char)p->pos[0]))
218 		p->pos++;
219 
220 	if (p->pos[0] == '#' || p->pos[0] == '\0') {
221 		p->cur_tok = TOK_EOL;
222 		p->pos = NULL;
223 	} else if (p->pos[0] == ',') {
224 		p->cur_tok = TOK_COMMA;
225 		p->pos++;
226 	} else if (p->pos[0] == '"') {
227 		for (s = p->pos+1; s[0]; s++) {
228 			if (s[0] == '"') {
229 				if (s[1] == '"')
230 					s++;
231 				else
232 					break;
233 			}
234 		}
235 		if (s[0] != '"' || !tok_buf_check(p, s - p->pos))
236 			return TOK_FAIL;
237 		dst = p->buf;
238 		for (s2 = p->pos+1; s2 < s; s2++) {
239 			*dst++ = *s2;
240 			if (*s2 == '"') s2++;
241 		}
242 		*dst = 0;
243 		p->pos = s + 1;
244 		p->cur_tok = TOK_STRING;
245 		p->cur_tok_str = p->buf;
246 	} else {
247 		for (s = p->pos + 1; *s; s++) {
248 			if (*s == ',' || *s == '#' || *s == '"')
249 				break;
250 			if (isspace((unsigned char)*s))
251 				break;
252 		}
253 		if (!tok_buf_check(p, s - p->pos + 1))
254 			return TOK_FAIL;
255 		memcpy(p->buf, p->pos, s - p->pos);
256 		p->buf[s - p->pos] = 0;
257 		p->pos = s;
258 		p->cur_tok = TOK_IDENT;
259 		p->cur_tok_str = p->buf;
260 
261 	}
262 	return p->cur_tok;
263 }
264 
eat(struct TokParser * p,enum TokType ttype)265 static bool eat(struct TokParser *p, enum TokType ttype)
266 {
267 	if (p->cur_tok == ttype) {
268 		next_token(p);
269 		return true;
270 	}
271 	return false;
272 }
273 
eat_kw(struct TokParser * p,const char * kw)274 static bool eat_kw(struct TokParser *p, const char *kw)
275 {
276 	if (p->cur_tok == TOK_IDENT && strcmp(kw, p->cur_tok_str) == 0) {
277 		next_token(p);
278 		return true;
279 	}
280 	return false;
281 }
282 
expect(struct TokParser * tp,enum TokType ttype,const char ** str_p)283 static bool expect(struct TokParser *tp, enum TokType ttype, const char **str_p)
284 {
285 	if (tp->cur_tok == ttype) {
286 		*str_p = tp->buf;
287 		return true;
288 	}
289 	return false;
290 }
291 
path_join(const char * p1,const char * p2)292 static char *path_join(const char *p1, const char *p2)
293 {
294 	size_t len1, len2;
295 	char *res = NULL, *pos;
296 
297 	if (p2[0] == '/' || p1[0] == 0 || !memcmp(p1, ".", 2))
298 		return strdup(p2);
299 	len1 = strlen(p1);
300 	len2 = strlen(p2);
301 	res = malloc(len1 + len2 + 2 + 1);
302 	if (res) {
303 		memcpy(res, p1, len1);
304 		pos = res + len1;
305 		if (pos[-1] != '/')
306 			*pos++ = '/';
307 		memcpy(pos, p2, len2 + 1);
308 	}
309 	return res;
310 }
311 
path_join_dirname(const char * parent,const char * fn)312 static char *path_join_dirname(const char *parent, const char *fn)
313 {
314 	char *tmp, *res;
315 	const char *basedir;
316 	if (fn[0] == '/')
317 		return strdup(fn);
318 	tmp = strdup(parent);
319 	if (!tmp)
320 		return NULL;
321 	basedir = dirname(tmp);
322 	res = path_join(basedir, fn);
323 	free(tmp);
324 	return res;
325 }
326 
init_parser(struct TokParser * p)327 static void init_parser(struct TokParser *p)
328 {
329 	memset(p, 0, sizeof(*p));
330 }
331 
parse_from_string(struct TokParser * p,const char * str)332 static void parse_from_string(struct TokParser *p, const char *str)
333 {
334 	p->pos = str;
335 	p->cur_tok = TOK_COMMA;
336 	p->cur_tok_str = NULL;
337 	next_token(p);
338 }
339 
free_parser(struct TokParser * p)340 static void free_parser(struct TokParser *p)
341 {
342 	free(p->buf);
343 	p->buf = NULL;
344 }
345 
346 static bool parse_names(struct HBAName *hname, struct TokParser *p, bool is_db, const char *parent_filename);
347 
parse_namefile(struct HBAName * hname,const char * fn,bool is_db)348 static bool parse_namefile(struct HBAName *hname, const char *fn, bool is_db)
349 {
350 	FILE *f;
351 	ssize_t len;
352 	char *ln = NULL;
353 	size_t buflen = 0;
354 	int linenr;
355 	bool ok = false;
356 	struct TokParser tp;
357 
358 	init_parser(&tp);
359 
360 	f = fopen(fn, "r");
361 	if (!f) {
362 		return false;
363 	}
364 	for (linenr = 1; ; linenr++) {
365 		len = getline(&ln, &buflen, f);
366 		if (len < 0) {
367 			ok = true;
368 			break;
369 		}
370 		parse_from_string(&tp, ln);
371 		if (!parse_names(hname, &tp, is_db, fn))
372 			break;
373 	}
374 	free_parser(&tp);
375 	free(ln);
376 	fclose(f);
377 	return ok;
378 }
379 
parse_names(struct HBAName * hname,struct TokParser * tp,bool is_db,const char * parent_filename)380 static bool parse_names(struct HBAName *hname, struct TokParser *tp, bool is_db, const char *parent_filename)
381 {
382 	const char *tok;
383 	while (1) {
384 		if (eat_kw(tp, "all")) {
385 			hname->flags |= NAME_ALL;
386 			goto eat_comma;
387 		}
388 		if (is_db) {
389 			if (eat_kw(tp, "sameuser")) {
390 				hname->flags |= NAME_SAMEUSER;
391 				goto eat_comma;
392 			}
393 			if (eat_kw(tp, "samerole")) {
394 				log_warning("samerole is not supported");
395 				return false;
396 			}
397 			if (eat_kw(tp, "samegroup")) {
398 				log_warning("samegroup is not supported");
399 				return false;
400 			}
401 			if (eat_kw(tp, "replication")) {
402 				log_warning("replication is not supported");
403 				return false;
404 			}
405 		}
406 
407 		if (expect(tp, TOK_IDENT, &tok)) {
408 			if (tok[0] == '+') {
409 				return false;
410 			}
411 
412 			if (tok[0] == '@') {
413 				bool ok;
414 				char *fn;
415 				fn = path_join_dirname(parent_filename, tok + 1);
416 				if (!fn)
417 					return false;
418 				ok = parse_namefile(hname, fn, is_db);
419 				free(fn);
420 				if (!ok)
421 					return false;
422 				next_token(tp);
423 				goto eat_comma;
424 			}
425 			/* fallthrough */
426 		} else if (expect(tp, TOK_STRING, &tok)) {
427 			/* fallthrough */
428 		} else {
429 			return false;
430 		}
431 
432 		/*
433 		 * TOK_IDENT or TOK_STRING as plain name.
434 		 */
435 
436 		if (!hname->name_set) {
437 			hname->name_set = strset_new(NULL);
438 			if (!hname->name_set)
439 				return false;
440 		}
441 		if (!strset_add(hname->name_set, tok, strlen(tok)))
442 			return false;
443 		next_token(tp);
444 eat_comma:
445 		if (!eat(tp, TOK_COMMA))
446 			break;
447 	}
448 	return true;
449 }
450 
rule_free(struct HBARule * rule)451 static void rule_free(struct HBARule *rule)
452 {
453 	strset_free(rule->db_name.name_set);
454 	strset_free(rule->user_name.name_set);
455 	free(rule);
456 }
457 
parse_addr(struct HBARule * rule,const char * addr)458 static bool parse_addr(struct HBARule *rule, const char *addr)
459 {
460 	if (inet_pton(AF_INET6, addr, rule->rule_addr)) {
461 		rule->rule_af = AF_INET6;
462 	} else if (inet_pton(AF_INET, addr, rule->rule_addr)) {
463 		rule->rule_af = AF_INET;
464 	} else {
465 		return false;
466 	}
467 	return true;
468 }
469 
parse_nmask(struct HBARule * rule,const char * nmask)470 static bool parse_nmask(struct HBARule *rule, const char *nmask)
471 {
472 	char *end = NULL;
473 	unsigned long bits;
474 	unsigned int i;
475 	errno = 0;
476 	bits = strtoul(nmask, &end, 10);
477 	if (errno || *end) {
478 		return false;
479 	}
480 	if (rule->rule_af == AF_INET && bits > 32) {
481 		return false;
482 	}
483 	if (rule->rule_af == AF_INET6 && bits > 128) {
484 		return false;
485 	}
486 	for (i = 0; i < bits/8; i++)
487 		rule->rule_mask[i] = 255;
488 	if (bits % 8)
489 		rule->rule_mask[i] = 255 << (8 - (bits % 8));
490 	return true;
491 }
492 
bad_mask(struct HBARule * rule)493 static bool bad_mask(struct HBARule *rule)
494 {
495 	int i, bytes = rule->rule_af == AF_INET ? 4 : 16;
496 	uint8_t res = 0;
497 	for (i = 0; i < bytes; i++)
498 		res |= rule->rule_addr[i] & (255 ^ rule->rule_mask[i]);
499 	return !!res;
500 }
501 
parse_line(struct HBA * hba,struct TokParser * tp,int linenr,const char * parent_filename)502 static bool parse_line(struct HBA *hba, struct TokParser *tp, int linenr, const char *parent_filename)
503 {
504 	const char *addr = NULL, *mask = NULL;
505 	enum RuleType rtype;
506 	char *nmask = NULL;
507 	struct HBARule *rule = NULL;
508 
509 	if (eat_kw(tp, "local")) {
510 		rtype = RULE_LOCAL;
511 	} else if (eat_kw(tp, "host")) {
512 		rtype = RULE_HOST;
513 	} else if (eat_kw(tp, "hostssl")) {
514 		rtype = RULE_HOSTSSL;
515 	} else if (eat_kw(tp, "hostnossl")) {
516 		rtype = RULE_HOSTNOSSL;
517 	} else if (eat(tp, TOK_EOL)) {
518 		return true;
519 	} else {
520 		log_warning("hba line %d: unknown type", linenr);
521 		return false;
522 	}
523 
524 	rule = calloc(sizeof *rule, 1);
525 	if (!rule) {
526 		log_warning("hba: no mem for rule");
527 		return false;
528 	}
529 	rule->rule_type = rtype;
530 
531 	if (!parse_names(&rule->db_name, tp, true, parent_filename))
532 		goto failed;
533 	if (!parse_names(&rule->user_name, tp, true, parent_filename))
534 		goto failed;
535 
536 	if (rtype == RULE_LOCAL) {
537 		rule->rule_af = AF_UNIX;
538 	} else {
539 		if (!expect(tp, TOK_IDENT, &addr)) {
540 			log_warning("hba line %d: did not find address - %d - '%s'", linenr, tp->cur_tok, tp->buf);
541 			goto failed;
542 		}
543 		nmask = strchr(addr, '/');
544 		if (nmask) {
545 			*nmask++ = 0;
546 		}
547 
548 		if (!parse_addr(rule, addr)) {
549 			log_warning("hba line %d: failed to parse address - %s", linenr, addr);
550 			goto failed;
551 		}
552 
553 		if (nmask) {
554 			if (!parse_nmask(rule, nmask)) {
555 				log_warning("hba line %d: invalid mask", linenr);
556 				goto failed;
557 			}
558 			next_token(tp);
559 		} else {
560 			next_token(tp);
561 			if (!expect(tp, TOK_IDENT, &mask)) {
562 				log_warning("hba line %d: did not find mask", linenr);
563 				goto failed;
564 			}
565 			if (!inet_pton(rule->rule_af, mask, rule->rule_mask)) {
566 				log_warning("hba line %d: failed to parse mask: %s", linenr, mask);
567 				goto failed;
568 			}
569 			next_token(tp);
570 		}
571 		if (bad_mask(rule)) {
572 			char buf1[128], buf2[128];
573 			log_warning("address does not match mask in %s line #%d: %s / %s", parent_filename, linenr,
574 				    inet_ntop(rule->rule_af, rule->rule_addr, buf1, sizeof buf1),
575 				    inet_ntop(rule->rule_af, rule->rule_mask, buf2, sizeof buf2));
576 		}
577 	}
578 
579 	if (eat_kw(tp, "trust")) {
580 		rule->rule_method = AUTH_TRUST;
581 	} else if (eat_kw(tp, "reject")) {
582 		rule->rule_method = AUTH_REJECT;
583 	} else if (eat_kw(tp, "md5")) {
584 		rule->rule_method = AUTH_MD5;
585 	} else if (eat_kw(tp, "password")) {
586 		rule->rule_method = AUTH_PLAIN;
587 	} else if (eat_kw(tp, "peer")) {
588 		rule->rule_method = AUTH_PEER;
589 	} else if (eat_kw(tp, "cert")) {
590 		rule->rule_method = AUTH_CERT;
591 	} else if (eat_kw(tp, "scram-sha-256")) {
592 		rule->rule_method = AUTH_SCRAM_SHA_256;
593 	} else {
594 		log_warning("hba line %d: unsupported method: buf=%s", linenr, tp->buf);
595 		goto failed;
596 	}
597 
598 	if (!eat(tp, TOK_EOL)) {
599 		log_warning("hba line %d: unsupported parameters", linenr);
600 		goto failed;
601 	}
602 
603 	list_append(&hba->rules, &rule->node);
604 	return true;
605 failed:
606 	rule_free(rule);
607 	return false;
608 }
609 
hba_load_rules(const char * fn)610 struct HBA *hba_load_rules(const char *fn)
611 {
612 	struct HBA *hba = NULL;
613 	FILE *f = NULL;
614 	char *ln = NULL;
615 	size_t lnbuf = 0;
616 	ssize_t len;
617 	int linenr;
618 	struct TokParser tp;
619 
620 	init_parser(&tp);
621 
622 	hba = malloc(sizeof *hba);
623 	if (!hba)
624 		goto out;
625 
626 	list_init(&hba->rules);
627 
628 	f = fopen(fn, "r");
629 	if (!f) {
630 		log_error("could not open hba config file %s: %s", fn, strerror(errno));
631 		goto out;
632 	}
633 
634 	for (linenr = 1; ; linenr++) {
635 		len = getline(&ln, &lnbuf, f);
636 		if (len < 0)
637 			break;
638 		parse_from_string(&tp, ln);
639 		if (!parse_line(hba, &tp, linenr, fn)) {
640 			/* Tell the admin where to look for the problem. */
641 			log_warning("could not parse hba config line %d", linenr);
642 			/* Ignore line, but parse to the end. */
643 			continue;
644 		}
645 	}
646 out:
647 	free_parser(&tp);
648 	free(ln);
649 	if (f)
650 		fclose(f);
651 	return hba;
652 }
653 
hba_free(struct HBA * hba)654 void hba_free(struct HBA *hba)
655 {
656 	struct List *el, *tmp;
657 	struct HBARule *rule;
658 	if (!hba)
659 		return;
660 	list_for_each_safe(el, &hba->rules, tmp) {
661 		rule = container_of(el, struct HBARule, node);
662 		list_del(&rule->node);
663 		rule_free(rule);
664 	}
665 	free(hba);
666 }
667 
name_match(struct HBAName * hname,const char * name,unsigned int namelen,const char * pair)668 static bool name_match(struct HBAName *hname, const char *name, unsigned int namelen, const char *pair)
669 {
670 	if (hname->flags & NAME_ALL)
671 		return true;
672 	if ((hname->flags & NAME_SAMEUSER) && strcmp(name, pair) == 0)
673 		return true;
674 	if (hname->name_set)
675 		return strset_contains(hname->name_set, name, namelen);
676 	return false;
677 }
678 
match_inet4(const struct HBARule * rule,PgAddr * addr)679 static bool match_inet4(const struct HBARule *rule, PgAddr *addr)
680 {
681 	const uint32_t *src, *base, *mask;
682 	if (pga_family(addr) != AF_INET)
683 		return false;
684 	src = (uint32_t *)&addr->sin.sin_addr.s_addr;
685 	base = (uint32_t *)rule->rule_addr;
686 	mask = (uint32_t *)rule->rule_mask;
687 	return (src[0] & mask[0]) == base[0];
688 }
689 
match_inet6(const struct HBARule * rule,PgAddr * addr)690 static bool match_inet6(const struct HBARule *rule, PgAddr *addr)
691 {
692 	const uint32_t *src, *base, *mask;
693 	if (pga_family(addr) != AF_INET6)
694 		return false;
695 	src = (uint32_t *)addr->sin6.sin6_addr.s6_addr;
696 	base = (uint32_t *)rule->rule_addr;
697 	mask = (uint32_t *)rule->rule_mask;
698 	return (src[0] & mask[0]) == base[0] && (src[1] & mask[1]) == base[1] &&
699 		(src[2] & mask[2]) == base[2] && (src[3] & mask[3]) == base[3];
700 }
701 
hba_eval(struct HBA * hba,PgAddr * addr,bool is_tls,const char * dbname,const char * username)702 int hba_eval(struct HBA *hba, PgAddr *addr, bool is_tls, const char *dbname, const char *username)
703 {
704 	struct List *el;
705 	struct HBARule *rule;
706 	unsigned int dbnamelen = strlen(dbname);
707 	unsigned int unamelen = strlen(username);
708 
709 	if (!hba)
710 		return AUTH_REJECT;
711 
712 	list_for_each(el, &hba->rules) {
713 		rule = container_of(el, struct HBARule, node);
714 
715 		/* match address */
716 		if (pga_is_unix(addr)) {
717 			if (rule->rule_type != RULE_LOCAL)
718 				continue;
719 		} else if (rule->rule_type == RULE_LOCAL) {
720 			continue;
721 		} else if (rule->rule_type == RULE_HOSTSSL && !is_tls) {
722 			continue;
723 		} else if (rule->rule_type == RULE_HOSTNOSSL && is_tls) {
724 			continue;
725 		} else if (rule->rule_af == AF_INET) {
726 			if (!match_inet4(rule, addr))
727 				continue;
728 		} else if (rule->rule_af == AF_INET6) {
729 			if (!match_inet6(rule, addr))
730 				continue;
731 		} else {
732 			continue;
733 		}
734 
735 		/* match db & user */
736 		if (!name_match(&rule->db_name, dbname, dbnamelen, username))
737 			continue;
738 		if (!name_match(&rule->user_name, username, unamelen, dbname))
739 			continue;
740 
741 		/* rule matches */
742 		return rule->rule_method;
743 	}
744 	return AUTH_REJECT;
745 }
746