hba.c 31.8 KB
Newer Older
1 2
/*-------------------------------------------------------------------------
 *
3
 * hba.c
4 5 6
 *	  Routines to handle host based authentication (that's the scheme
 *	  wherein you authenticate a user by seeing what IP address the system
 *	  says he comes from and possibly using ident).
7
 *
Bruce Momjian's avatar
Bruce Momjian committed
8
 * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
9 10 11 12
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
13
 *	  $Header: /cvsroot/pgsql/src/backend/libpq/hba.c,v 1.91 2002/12/11 22:17:11 momjian Exp $
14 15 16
 *
 *-------------------------------------------------------------------------
 */
17 18
#include "postgres.h"

19
#include <errno.h>
20
#include <pwd.h>
Bruce Momjian's avatar
Bruce Momjian committed
21
#include <fcntl.h>
22
#include <sys/param.h>
23 24 25
#include <sys/socket.h>
#if defined(HAVE_STRUCT_CMSGCRED) || defined(HAVE_STRUCT_FCRED) || defined(HAVE_STRUCT_SOCKCRED)
#include <sys/uio.h>
26 27
#include <sys/ucred.h>
#endif
28 29
#include <netinet/in.h>
#include <arpa/inet.h>
30
#include <unistd.h>
31

Bruce Momjian's avatar
Bruce Momjian committed
32 33
#include "commands/user.h"
#include "libpq/crypt.h"
34
#include "libpq/libpq.h"
Bruce Momjian's avatar
Bruce Momjian committed
35
#include "miscadmin.h"
36
#include "nodes/pg_list.h"
37
#include "storage/fd.h"
38

39 40

#define IDENT_USERNAME_MAX 512
41
/* Max size of username ident server can return */
42

Bruce Momjian's avatar
Bruce Momjian committed
43
/* This is used to separate values in multi-valued column strings */
Bruce Momjian's avatar
Bruce Momjian committed
44
#define MULTI_VALUE_SEP "\001"
Bruce Momjian's avatar
Bruce Momjian committed
45

46 47 48
/*
 * These variables hold the pre-parsed contents of the hba and ident
 * configuration files.  Each is a list of sublists, one sublist for
49
 * each (non-empty, non-comment) line of the file.	Each sublist's
50 51 52 53 54 55
 * first item is an integer line number (so we can give somewhat-useful
 * location info in error messages).  Remaining items are palloc'd strings,
 * one string per token on the line.  Note there will always be at least
 * one token, since blank lines are not entered in the data structure.
 */
static List *hba_lines = NIL;	/* pre-parsed contents of hba file */
56
static List *ident_lines = NIL; /* pre-parsed contents of ident file */
Bruce Momjian's avatar
Bruce Momjian committed
57 58 59
static List *group_lines = NIL; /* pre-parsed contents of group file */
static List *user_lines = NIL;	/* pre-parsed contents of user password
								 * file */
Bruce Momjian's avatar
Bruce Momjian committed
60 61

/* sorted entries so we can do binary search lookups */
Bruce Momjian's avatar
Bruce Momjian committed
62 63 64 65 66
static List **user_sorted = NULL;		/* sorted user list, for bsearch() */
static List **group_sorted = NULL;		/* sorted group list, for
										 * bsearch() */
static int	user_length;
static int	group_length;
67

Bruce Momjian's avatar
Bruce Momjian committed
68 69
static List *tokenize_file(FILE *file);
static char *tokenize_inc_file(const char *inc_filename);
70

71 72 73 74
/*
 * Some standard C libraries, including GNU, have an isblank() function.
 * Others, including Solaris, do not.  So we have our own.
 */
75
static bool
76 77
isblank(const char c)
{
78
	return c == ' ' || c == '\t' || c == '\r';
79 80
}

81

82
/*
Bruce Momjian's avatar
Bruce Momjian committed
83 84 85 86 87 88 89 90
 *	 Grab one token out of fp. Tokens are strings of non-blank
 *	 characters bounded by blank characters, beginning of line, and
 *	 end of line. Blank means space or tab. Return the token as
 *	 *buf. Leave file positioned to character immediately after the
 *	 token or EOF, whichever comes first. If no more tokens on line,
 *	 return null string as *buf and position file to beginning of
 *	 next line or EOF, whichever comes first. Allow spaces in quoted
 *	 strings. Terminate on unquoted commas. Handle comments.
91
 */
Bruce Momjian's avatar
Bruce Momjian committed
92
void
93
next_token(FILE *fp, char *buf, const int bufsz)
94
{
95
	int			c;
96
	char	   *start_buf = buf;
Bruce Momjian's avatar
Bruce Momjian committed
97 98 99
	char	   *end_buf = buf + (bufsz - 1);
	bool		in_quote = false;
	bool		was_quote = false;
100

Bruce Momjian's avatar
Bruce Momjian committed
101 102
	/* Move over initial whitespace and commas */
	while ((c = getc(fp)) != EOF && (isblank(c) || c == ','))
103
		;
104

105
	if (c != EOF && c != '\n')
106 107
	{
		/*
Bruce Momjian's avatar
Bruce Momjian committed
108 109
		 * Build a token in buf of next characters up to EOF, EOL,
		 * unquoted comma, or unquoted whitespace.
110
		 */
Bruce Momjian's avatar
Bruce Momjian committed
111 112
		while (c != EOF && c != '\n' &&
			   (!isblank(c) || in_quote == true))
113
		{
Bruce Momjian's avatar
Bruce Momjian committed
114 115 116 117 118
			/* skip comments to EOL */
			if (c == '#' && !in_quote)
			{
				while ((c = getc(fp)) != EOF && c != '\n')
					;
119 120 121 122
				/* If only comment, consume EOL too; return EOL */
				if (c != EOF && buf == start_buf)
					c = getc(fp);
				break;
Bruce Momjian's avatar
Bruce Momjian committed
123 124 125 126 127 128 129 130 131 132 133 134 135
			}

			if (buf >= end_buf)
			{
				elog(LOG, "Token too long in authentication file, skipping, %s", buf);
				/* Discard remainder of line */
				while ((c = getc(fp)) != EOF && c != '\n')
					;
				buf[0] = '\0';
				break;
			}

			if (c != '"' || (c == '"' && was_quote))
136
				*buf++ = c;
Bruce Momjian's avatar
Bruce Momjian committed
137 138 139 140 141 142

			/* We pass back the comma so the caller knows there is more */
			if ((isblank(c) || c == ',') && !in_quote)
				break;

			/* Literal double-quote is two double-quotes */
143
			if (in_quote && c == '"')
Bruce Momjian's avatar
Bruce Momjian committed
144 145 146 147
				was_quote = !was_quote;
			else
				was_quote = false;

148 149 150
			if (c == '"')
				in_quote = !in_quote;

151 152
			c = getc(fp);
		}
153

154
		/*
155
		 * Put back the char right after the token (critical in case it is
Bruce Momjian's avatar
Bruce Momjian committed
156
		 * EOL, since we need to detect end-of-line at next call).
157
		 */
158 159
		if (c != EOF)
			ungetc(c, fp);
160 161
	}
	*buf = '\0';
162 163
}

Bruce Momjian's avatar
Bruce Momjian committed
164
/*
Bruce Momjian's avatar
Bruce Momjian committed
165 166 167
 *	 Tokenize file and handle file inclusion and comma lists. We have
 *	 to  break	apart  the	commas	to	expand	any  file names then
 *	 reconstruct with commas.
Bruce Momjian's avatar
Bruce Momjian committed
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
 */
static char *
next_token_expand(FILE *file)
{
	char		buf[MAX_TOKEN];
	char	   *comma_str = pstrdup("");
	bool		trailing_comma;
	char	   *incbuf;

	do
	{
		next_token(file, buf, sizeof(buf));
		if (!*buf)
			break;

Bruce Momjian's avatar
Bruce Momjian committed
183
		if (buf[strlen(buf) - 1] == ',')
Bruce Momjian's avatar
Bruce Momjian committed
184 185
		{
			trailing_comma = true;
Bruce Momjian's avatar
Bruce Momjian committed
186
			buf[strlen(buf) - 1] = '\0';
Bruce Momjian's avatar
Bruce Momjian committed
187 188 189 190 191 192
		}
		else
			trailing_comma = false;

		/* Is this referencing a file? */
		if (buf[0] == '@')
Bruce Momjian's avatar
Bruce Momjian committed
193
			incbuf = tokenize_inc_file(buf + 1);
Bruce Momjian's avatar
Bruce Momjian committed
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
		else
			incbuf = pstrdup(buf);

		comma_str = repalloc(comma_str,
							 strlen(comma_str) + strlen(incbuf) + 1);
		strcat(comma_str, incbuf);
		pfree(incbuf);

		if (trailing_comma)
		{
			comma_str = repalloc(comma_str, strlen(comma_str) + 1 + 1);
			strcat(comma_str, MULTI_VALUE_SEP);
		}
	} while (trailing_comma);

	return comma_str;
}

212

Bruce Momjian's avatar
Bruce Momjian committed
213 214 215
/*
 * Free memory used by lines/tokens (i.e., structure built by tokenize_file)
 */
216
static void
Bruce Momjian's avatar
Bruce Momjian committed
217
free_lines(List **lines)
218
{
Bruce Momjian's avatar
Bruce Momjian committed
219 220 221 222
	if (*lines)
	{
		List	   *line,
				   *token;
223

Bruce Momjian's avatar
Bruce Momjian committed
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246
		foreach(line, *lines)
		{
			List	   *ln = lfirst(line);

			/* free the pstrdup'd tokens (don't try it on the line number) */
			foreach(token, lnext(ln))
				pfree(lfirst(token));
			/* free the sublist structure itself */
			freeList(ln);
		}
		/* free the list structure itself */
		freeList(*lines);
		/* clear the static variable */
		*lines = NIL;
	}
}


static char *
tokenize_inc_file(const char *inc_filename)
{
	char	   *inc_fullname;
	FILE	   *inc_file;
Bruce Momjian's avatar
Bruce Momjian committed
247
	List	   *inc_lines;
Bruce Momjian's avatar
Bruce Momjian committed
248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287
	List	   *line;
	char	   *comma_str = pstrdup("");

	inc_fullname = (char *) palloc(strlen(DataDir) + 1 +
								   strlen(inc_filename) + 1);
	strcpy(inc_fullname, DataDir);
	strcat(inc_fullname, "/");
	strcat(inc_fullname, inc_filename);

	inc_file = AllocateFile(inc_fullname, "r");
	if (!inc_file)
	{
		elog(LOG, "tokenize_inc_file: Unable to open secondary authentication file \"@%s\" as \"%s\": %m",
			 inc_filename, inc_fullname);
		pfree(inc_fullname);

		/* return empty string, it matches nothing */
		return pstrdup("");
	}
	pfree(inc_fullname);

	/* There is possible recursion here if the file contains @ */
	inc_lines = tokenize_file(inc_file);
	FreeFile(inc_file);

	/* Create comma-separate string from List */
	foreach(line, inc_lines)
	{
		List	   *ln = lfirst(line);
		List	   *token;

		/* First entry is line number */
		foreach(token, lnext(ln))
		{
			if (strlen(comma_str))
			{
				comma_str = repalloc(comma_str, strlen(comma_str) + 1);
				strcat(comma_str, MULTI_VALUE_SEP);
			}
			comma_str = repalloc(comma_str,
Bruce Momjian's avatar
Bruce Momjian committed
288
						  strlen(comma_str) + strlen(lfirst(token)) + 1);
Bruce Momjian's avatar
Bruce Momjian committed
289 290 291 292 293 294 295
			strcat(comma_str, lfirst(token));
		}
	}

	free_lines(&inc_lines);

	return comma_str;
296 297 298
}


Bruce Momjian's avatar
Bruce Momjian committed
299

300
/*
301
 *	Read the given file and create a list of line sublists.
302
 */
303 304
static List *
tokenize_file(FILE *file)
305
{
306 307 308
	List	   *lines = NIL;
	List	   *next_line = NIL;
	int			line_number = 1;
Bruce Momjian's avatar
Bruce Momjian committed
309
	char	   *buf;
310

311
	while (!feof(file))
312
	{
Bruce Momjian's avatar
Bruce Momjian committed
313
		buf = next_token_expand(file);
314

Bruce Momjian's avatar
Bruce Momjian committed
315
		/* add token to list, unless we are at EOL or comment start */
316
		if (buf[0] != '\0')
317 318 319 320
		{
			if (next_line == NIL)
			{
				/* make a new line List */
321 322
				next_line = makeListi1(line_number);
				lines = lappend(lines, next_line);
323
			}
324
			/* append token to current line's list */
Bruce Momjian's avatar
Bruce Momjian committed
325
			next_line = lappend(next_line, buf);
326 327
		}
		else
328
		{
Bruce Momjian's avatar
Bruce Momjian committed
329
			/* we are at real or logical EOL, so force a new line List */
330
			next_line = NIL;
331
		}
332

Bruce Momjian's avatar
Bruce Momjian committed
333
		/* Advance line number whenever we reach EOL */
334 335
		if (next_line == NIL)
			line_number++;
336
	}
337 338

	return lines;
339 340 341 342
}


/*
343
 * Compare two lines based on their user/group names.
Bruce Momjian's avatar
Bruce Momjian committed
344
 *
345
 * Used for qsort() sorting.
346
 */
Bruce Momjian's avatar
Bruce Momjian committed
347
static int
348
user_group_qsort_cmp(const void *list1, const void *list2)
349
{
Bruce Momjian's avatar
Bruce Momjian committed
350 351 352
	/* first node is line number */
	char	   *user1 = lfirst(lnext(*(List **) list1));
	char	   *user2 = lfirst(lnext(*(List **) list2));
Bruce Momjian's avatar
Bruce Momjian committed
353 354 355 356 357

	return strcmp(user1, user2);
}


358 359 360 361 362 363 364 365
/*
 * Compare two lines based on their user/group names.
 *
 * Used for bsearch() lookup.
 */
static int
user_group_bsearch_cmp(const void *user, const void *list)
{
Bruce Momjian's avatar
Bruce Momjian committed
366 367
	/* first node is line number */
	char	   *user2 = lfirst(lnext(*(List **) list));
368 369 370 371 372

	return strcmp(user, user2);
}


Bruce Momjian's avatar
Bruce Momjian committed
373 374 375 376 377 378 379
/*
 * Lookup a group name in the pg_group file
 */
static List **
get_group_line(const char *group)
{
	return (List **) bsearch((void *) group,
Bruce Momjian's avatar
Bruce Momjian committed
380 381 382 383
							 (void *) group_sorted,
							 group_length,
							 sizeof(List *),
							 user_group_bsearch_cmp);
Bruce Momjian's avatar
Bruce Momjian committed
384 385 386 387 388 389
}


/*
 * Lookup a user name in the pg_shadow file
 */
Bruce Momjian's avatar
Bruce Momjian committed
390
List	  **
Bruce Momjian's avatar
Bruce Momjian committed
391 392 393
get_user_line(const char *user)
{
	return (List **) bsearch((void *) user,
Bruce Momjian's avatar
Bruce Momjian committed
394 395 396 397
							 (void *) user_sorted,
							 user_length,
							 sizeof(List *),
							 user_group_bsearch_cmp);
Bruce Momjian's avatar
Bruce Momjian committed
398 399 400 401 402 403 404 405 406
}


/*
 * Check group for a specific user.
 */
static int
check_group(char *group, char *user)
{
Bruce Momjian's avatar
Bruce Momjian committed
407 408
	List	  **line,
			   *l;
Bruce Momjian's avatar
Bruce Momjian committed
409 410

	if ((line = get_group_line(group)) != NULL)
411
	{
Bruce Momjian's avatar
Bruce Momjian committed
412 413
		foreach(l, lnext(lnext(*line)))
			if (strcmp(lfirst(l), user) == 0)
Bruce Momjian's avatar
Bruce Momjian committed
414
			return 1;
Bruce Momjian's avatar
Bruce Momjian committed
415
	}
416

Bruce Momjian's avatar
Bruce Momjian committed
417 418 419 420 421 422 423 424 425
	return 0;
}

/*
 * Check comma user list for a specific user, handle group names.
 */
static int
check_user(char *user, char *param_str)
{
Bruce Momjian's avatar
Bruce Momjian committed
426
	char	   *tok;
Bruce Momjian's avatar
Bruce Momjian committed
427 428 429 430

	for (tok = strtok(param_str, MULTI_VALUE_SEP); tok != NULL; tok = strtok(NULL, MULTI_VALUE_SEP))
	{
		if (tok[0] == '+')
431
		{
Bruce Momjian's avatar
Bruce Momjian committed
432
			if (check_group(tok + 1, user))
Bruce Momjian's avatar
Bruce Momjian committed
433 434 435
				return 1;
		}
		else if (strcmp(tok, user) == 0 ||
Bruce Momjian's avatar
Bruce Momjian committed
436
				 strcmp(tok, "all") == 0)
Bruce Momjian's avatar
Bruce Momjian committed
437 438
			return 1;
	}
439

Bruce Momjian's avatar
Bruce Momjian committed
440 441 442 443 444 445 446 447 448
	return 0;
}

/*
 * Check to see if db/user combination matches param string.
 */
static int
check_db(char *dbname, char *user, char *param_str)
{
Bruce Momjian's avatar
Bruce Momjian committed
449
	char	   *tok;
Bruce Momjian's avatar
Bruce Momjian committed
450 451 452 453 454 455 456 457 458

	for (tok = strtok(param_str, MULTI_VALUE_SEP); tok != NULL; tok = strtok(NULL, MULTI_VALUE_SEP))
	{
		if (strcmp(tok, "all") == 0)
			return 1;
		else if (strcmp(tok, "sameuser") == 0)
		{
			if (strcmp(dbname, user) == 0)
				return 1;
459
		}
Bruce Momjian's avatar
Bruce Momjian committed
460 461 462 463 464 465 466
		else if (strcmp(tok, "samegroup") == 0)
		{
			if (check_group(dbname, user))
				return 1;
		}
		else if (strcmp(tok, dbname) == 0)
			return 1;
467
	}
Bruce Momjian's avatar
Bruce Momjian committed
468
	return 0;
469 470 471 472
}


/*
473 474 475
 *	Scan the rest of a host record (after the mask field)
 *	and return the interpretation of it as *userauth_p, auth_arg, and
 *	*error_p.  line points to the next token of the line.
476 477
 */
static void
478 479
parse_hba_auth(List *line, UserAuth *userauth_p, char *auth_arg,
			   bool *error_p)
480
{
481
	char	   *token;
482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500

	if (!line)
		*error_p = true;
	else
	{
		/* Get authentication type token. */
		token = lfirst(line);
		if (strcmp(token, "trust") == 0)
			*userauth_p = uaTrust;
		else if (strcmp(token, "ident") == 0)
			*userauth_p = uaIdent;
		else if (strcmp(token, "password") == 0)
			*userauth_p = uaPassword;
		else if (strcmp(token, "krb4") == 0)
			*userauth_p = uaKrb4;
		else if (strcmp(token, "krb5") == 0)
			*userauth_p = uaKrb5;
		else if (strcmp(token, "reject") == 0)
			*userauth_p = uaReject;
501
		else if (strcmp(token, "md5") == 0)
502
			*userauth_p = uaMD5;
503 504
		else if (strcmp(token, "crypt") == 0)
			*userauth_p = uaCrypt;
Bruce Momjian's avatar
Bruce Momjian committed
505 506 507 508
#ifdef USE_PAM
		else if (strcmp(token, "pam") == 0)
			*userauth_p = uaPAM;
#endif
509 510
		else
			*error_p = true;
511
		line = lnext(line);
512 513
	}

514
	if (!*error_p)
515
	{
516
		/* Get the authentication argument token, if any */
517
		if (!line)
518
			auth_arg[0] = '\0';
519 520
		else
		{
521
			StrNCpy(auth_arg, lfirst(line), MAX_AUTH_ARG - 1);
522 523
			/* If there is more on the line, it is an error */
			if (lnext(line))
524 525 526
				*error_p = true;
		}
	}
527 528 529
}


530
/*
531
 *	Process one line from the hba config file.
532
 *
533 534 535 536
 *	See if it applies to a connection from a host with IP address port->raddr
 *	to a database named port->database.  If so, return *found_p true
 *	and fill in the auth arguments into the appropriate port fields.
 *	If not, leave *found_p as it was.  If the record has a syntax error,
537
 *	return *error_p true, after issuing a message to the log.  If no error,
538
 *	leave *error_p as it was.
539
 */
540
static void
541
parse_hba(List *line, hbaPort *port, bool *found_p, bool *error_p)
542
{
543
	int			line_number;
544 545
	char	   *token;
	char	   *db;
Bruce Momjian's avatar
Bruce Momjian committed
546
	char	   *user;
547

548
	Assert(line != NIL);
549 550 551
	line_number = lfirsti(line);
	line = lnext(line);
	Assert(line != NIL);
552
	/* Check the record type. */
553
	token = lfirst(line);
554
	if (strcmp(token, "local") == 0)
555 556
	{
		/* Get the database. */
557 558 559 560 561
		line = lnext(line);
		if (!line)
			goto hba_syntax;
		db = lfirst(line);

Bruce Momjian's avatar
Bruce Momjian committed
562 563 564 565 566 567
		/* Get the user. */
		line = lnext(line);
		if (!line)
			goto hba_syntax;
		user = lfirst(line);

568 569 570
		line = lnext(line);
		if (!line)
			goto hba_syntax;
Bruce Momjian's avatar
Bruce Momjian committed
571 572

		/* Read the rest of the line. */
573
		parse_hba_auth(line, &port->auth_method, port->auth_arg, error_p);
574 575
		if (*error_p)
			goto hba_syntax;
576 577

		/*
578
		 * Disallow auth methods that always need AF_INET sockets to work.
579
		 */
580 581
		if (port->auth_method == uaKrb4 ||
			port->auth_method == uaKrb5)
582
			goto hba_syntax;
583

Bruce Momjian's avatar
Bruce Momjian committed
584
		if (port->raddr.sa.sa_family != AF_UNIX)
585 586
			return;
	}
587
	else if (strcmp(token, "host") == 0 || strcmp(token, "hostssl") == 0)
588
	{
589 590 591
		struct in_addr file_ip_addr,
					mask;

592
		if (strcmp(token, "hostssl") == 0)
593
		{
594 595
#ifdef USE_SSL
			/* Record does not match if we are not on an SSL connection */
596
			if (!port->ssl)
597
				return;
598 599 600 601 602

			/* Placeholder to require specific SSL level, perhaps? */
			/* Or a client certificate */

			/* Since we were on SSL, proceed as with normal 'host' mode */
603
#else
604
			/* We don't accept this keyword at all if no SSL support */
605
			goto hba_syntax;
606
#endif
607
		}
608 609

		/* Get the database. */
610 611 612 613
		line = lnext(line);
		if (!line)
			goto hba_syntax;
		db = lfirst(line);
614

Bruce Momjian's avatar
Bruce Momjian committed
615 616 617 618 619 620
		/* Get the user. */
		line = lnext(line);
		if (!line)
			goto hba_syntax;
		user = lfirst(line);

621
		/* Read the IP address field. */
622 623 624 625
		line = lnext(line);
		if (!line)
			goto hba_syntax;
		token = lfirst(line);
626 627
		if (!inet_aton(token, &file_ip_addr))
			goto hba_syntax;
628 629

		/* Read the mask field. */
630 631 632 633
		line = lnext(line);
		if (!line)
			goto hba_syntax;
		token = lfirst(line);
634 635
		if (!inet_aton(token, &mask))
			goto hba_syntax;
636

637
		/* Read the rest of the line. */
638 639 640
		line = lnext(line);
		if (!line)
			goto hba_syntax;
641
		parse_hba_auth(line, &port->auth_method, port->auth_arg, error_p);
642
		if (*error_p)
643
			goto hba_syntax;
644

Bruce Momjian's avatar
Bruce Momjian committed
645
		/* Must meet network restrictions */
646 647
		if (port->raddr.sa.sa_family != AF_INET ||
			((file_ip_addr.s_addr ^ port->raddr.in.sin_addr.s_addr) & mask.s_addr) != 0)
648
			return;
649
	}
650
	else
651
		goto hba_syntax;
652

Bruce Momjian's avatar
Bruce Momjian committed
653 654 655 656 657
	if (!check_db(port->database, port->user, db))
		return;
	if (!check_user(port->user, user))
		return;

658 659
	/* Success */
	*found_p = true;
660 661
	return;

662
hba_syntax:
663 664 665
	elog(LOG, "parse_hba: invalid syntax in pg_hba.conf file at line %d, token \"%s\"",
		 line_number,
		 line ? (const char *) lfirst(line) : "(end of line)");
666 667

	*error_p = true;
668
	return;
669 670 671
}


672
/*
673
 *	Scan the (pre-parsed) hba file line by line, looking for a match
674
 *	to the port's connection request.
675 676 677
 */
static bool
check_hba(hbaPort *port)
678
{
679 680 681
	bool		found_entry = false;
	bool		error = false;
	List	   *line;
Bruce Momjian's avatar
Bruce Momjian committed
682

683
	foreach(line, hba_lines)
684 685 686 687
	{
		parse_hba(lfirst(line), port, &found_entry, &error);
		if (found_entry || error)
			break;
688
	}
689

690 691
	if (!error)
	{
692
		/* If no matching entry was found, synthesize 'reject' entry. */
693
		if (!found_entry)
694
			port->auth_method = uaReject;
695
		return true;
696
	}
697 698
	else
		return false;
699
}
700 701


Bruce Momjian's avatar
Bruce Momjian committed
702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753

/*
 * Open the group file if possible (return NULL if not)
 */
static FILE *
group_openfile(void)
{
	char	   *filename;
	FILE	   *groupfile;

	filename = group_getfilename();
	groupfile = AllocateFile(filename, "r");

	if (groupfile == NULL && errno != ENOENT)
		elog(LOG, "could not open %s: %m", filename);

	pfree(filename);

	return groupfile;
}



/*
 * Open the password file if possible (return NULL if not)
 */
static FILE *
user_openfile(void)
{
	char	   *filename;
	FILE	   *pwdfile;

	filename = user_getfilename();
	pwdfile = AllocateFile(filename, "r");

	if (pwdfile == NULL && errno != ENOENT)
		elog(LOG, "could not open %s: %m", filename);

	pfree(filename);

	return pwdfile;
}



/*
 *	 Load group/user name mapping file
 */
void
load_group()
{
	FILE	   *group_file;
Bruce Momjian's avatar
Bruce Momjian committed
754
	List	   *line;
Bruce Momjian's avatar
Bruce Momjian committed
755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770

	if (group_lines)
		free_lines(&group_lines);

	group_file = group_openfile();
	if (!group_file)
		return;
	group_lines = tokenize_file(group_file);
	FreeFile(group_file);

	/* create sorted lines for binary searching */
	if (group_sorted)
		pfree(group_sorted);
	group_length = length(group_lines);
	if (group_length)
	{
Bruce Momjian's avatar
Bruce Momjian committed
771
		int			i = 0;
Bruce Momjian's avatar
Bruce Momjian committed
772 773 774 775 776 777

		group_sorted = palloc(group_length * sizeof(List *));

		foreach(line, group_lines)
			group_sorted[i++] = lfirst(line);

778
		qsort((void *) group_sorted, group_length, sizeof(List *), user_group_qsort_cmp);
Bruce Momjian's avatar
Bruce Momjian committed
779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808
	}
	else
		group_sorted = NULL;
}


/*
 *	 Load user/password mapping file
 */
void
load_user()
{
	FILE	   *user_file;
	List	   *line;

	if (user_lines)
		free_lines(&user_lines);

	user_file = user_openfile();
	if (!user_file)
		return;
	user_lines = tokenize_file(user_file);
	FreeFile(user_file);

	/* create sorted lines for binary searching */
	if (user_sorted)
		pfree(user_sorted);
	user_length = length(user_lines);
	if (user_length)
	{
Bruce Momjian's avatar
Bruce Momjian committed
809
		int			i = 0;
Bruce Momjian's avatar
Bruce Momjian committed
810 811 812 813 814 815

		user_sorted = palloc(user_length * sizeof(List *));

		foreach(line, user_lines)
			user_sorted[i++] = lfirst(line);

816
		qsort((void *) user_sorted, user_length, sizeof(List *), user_group_qsort_cmp);
Bruce Momjian's avatar
Bruce Momjian committed
817 818 819 820 821 822
	}
	else
		user_sorted = NULL;
}


Marc G. Fournier's avatar
Marc G. Fournier committed
823
/*
824
 * Read the config file and create a List of Lists of tokens in the file.
Marc G. Fournier's avatar
Marc G. Fournier committed
825
 * If we find a file by the old name of the config file (pg_hba), we issue
Bruce Momjian's avatar
Bruce Momjian committed
826
 * an error message because it probably needs to be converted.	He didn't
Marc G. Fournier's avatar
Marc G. Fournier committed
827 828 829
 * follow directions and just installed his old hba file in the new database
 * system.
 */
Bruce Momjian's avatar
Bruce Momjian committed
830
void
831
load_hba(void)
832
{
Bruce Momjian's avatar
Bruce Momjian committed
833
	int			bufsize;
Bruce Momjian's avatar
Bruce Momjian committed
834
	FILE	   *file;			/* The config file we have to read */
Bruce Momjian's avatar
Bruce Momjian committed
835
	char	   *conf_file;		/* The name of the config file */
836

837 838
	if (hba_lines)
		free_lines(&hba_lines);
839

Bruce Momjian's avatar
Bruce Momjian committed
840 841 842 843
	/* Put together the full pathname to the config file. */
	bufsize = (strlen(DataDir) + strlen(CONF_FILE) + 2) * sizeof(char);
	conf_file = (char *) palloc(bufsize);
	snprintf(conf_file, bufsize, "%s/%s", DataDir, CONF_FILE);
844

Bruce Momjian's avatar
Bruce Momjian committed
845 846
	file = AllocateFile(conf_file, "r");
	if (file == NULL)
847
	{
Bruce Momjian's avatar
Bruce Momjian committed
848 849 850 851
		/* The open of the config file failed.	*/
		elog(LOG, "load_hba: Unable to open authentication config file \"%s\": %m",
			 conf_file);
		pfree(conf_file);
852 853 854
	}
	else
	{
Bruce Momjian's avatar
Bruce Momjian committed
855 856
		hba_lines = tokenize_file(file);
		FreeFile(file);
857
	}
Bruce Momjian's avatar
Bruce Momjian committed
858
	pfree(conf_file);
859 860 861
}


862
/*
863 864
 *	Process one line from the ident config file.
 *
865
 *	Take the line and compare it to the needed map, pg_user and ident_user.
866
 *	*found_p and *error_p are set according to our results.
867
 */
868
static void
869
parse_ident_usermap(List *line, const char *usermap_name, const char *pg_user,
870
					const char *ident_user, bool *found_p, bool *error_p)
871
{
872
	int			line_number;
873 874 875 876
	char	   *token;
	char	   *file_map;
	char	   *file_pguser;
	char	   *file_ident_user;
877 878

	*found_p = false;
879
	*error_p = false;
880 881

	Assert(line != NIL);
882 883 884 885 886
	line_number = lfirsti(line);
	line = lnext(line);
	Assert(line != NIL);

	/* Get the map token (must exist) */
887 888 889
	token = lfirst(line);
	file_map = token;

890
	/* Get the ident user token (must be provided) */
891 892 893 894
	line = lnext(line);
	if (!line)
		goto ident_syntax;
	token = lfirst(line);
895
	file_ident_user = token;
896

897 898 899 900 901 902 903 904 905 906 907 908
	/* Get the PG username token */
	line = lnext(line);
	if (!line)
		goto ident_syntax;
	token = lfirst(line);
	file_pguser = token;

	/* Match? */
	if (strcmp(file_map, usermap_name) == 0 &&
		strcmp(file_pguser, pg_user) == 0 &&
		strcmp(file_ident_user, ident_user) == 0)
		*found_p = true;
909 910 911
	return;

ident_syntax:
912 913 914
	elog(LOG, "parse_ident_usermap: invalid syntax in pg_ident.conf file at line %d, token \"%s\"",
		 line_number,
		 line ? (const char *) lfirst(line) : "(end of line)");
915

916 917 918 919 920 921
	*error_p = true;
	return;
}


/*
922
 *	Scan the (pre-parsed) ident usermap file line by line, looking for a match
923
 *
924 925
 *	See if the user with ident username "ident_user" is allowed to act
 *	as Postgres user "pguser" according to usermap "usermap_name".
926
 *
927 928 929
 *	Special case: For usermap "sameuser", don't look in the usermap
 *	file.  That's an implied map where "pguser" must be identical to
 *	"ident_user" in order to be authorized.
930
 *
931
 *	Iff authorized, return true.
932 933 934 935 936 937
 */
static bool
check_ident_usermap(const char *usermap_name,
					const char *pg_user,
					const char *ident_user)
{
938 939 940
	List	   *line;
	bool		found_entry = false,
				error = false;
941 942 943

	if (usermap_name[0] == '\0')
	{
944
		elog(LOG, "check_ident_usermap: hba configuration file does not "
Bruce Momjian's avatar
Bruce Momjian committed
945 946
		   "have the usermap field filled in in the entry that pertains "
		  "to this connection.  That field is essential for Ident-based "
947
			 "authentication.");
948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971
		found_entry = false;
	}
	else if (strcmp(usermap_name, "sameuser") == 0)
	{
		if (strcmp(pg_user, ident_user) == 0)
			found_entry = true;
		else
			found_entry = false;
	}
	else
	{
		foreach(line, ident_lines)
		{
			parse_ident_usermap(lfirst(line), usermap_name, pg_user,
								ident_user, &found_entry, &error);
			if (found_entry || error)
				break;
		}
	}
	return found_entry;
}


/*
972
 * Read the ident config file and create a List of Lists of tokens in the file.
973
 */
Bruce Momjian's avatar
Bruce Momjian committed
974
void
975
load_ident(void)
976
{
977 978 979
	FILE	   *file;			/* The map file we have to read */
	char	   *map_file;		/* The name of the map file we have to
								 * read */
980
	int			bufsize;
981

982 983 984 985 986 987 988 989
	if (ident_lines)
		free_lines(&ident_lines);

	/* put together the full pathname to the map file */
	bufsize = (strlen(DataDir) + strlen(USERMAP_FILE) + 2) * sizeof(char);
	map_file = (char *) palloc(bufsize);
	snprintf(map_file, bufsize, "%s/%s", DataDir, USERMAP_FILE);

Bruce Momjian's avatar
Bruce Momjian committed
990
	file = AllocateFile(map_file, "r");
991 992 993
	if (file == NULL)
	{
		/* The open of the map file failed.  */
994 995
		elog(LOG, "load_ident: Unable to open usermap file \"%s\": %m",
			 map_file);
996 997 998
	}
	else
	{
999
		ident_lines = tokenize_file(file);
1000 1001 1002 1003 1004 1005 1006
		FreeFile(file);
	}
	pfree(map_file);
}


/*
1007
 *	Parse the string "*ident_response" as a response from a query to an Ident
Bruce Momjian's avatar
Bruce Momjian committed
1008
 *	server.  If it's a normal response indicating a user name, return true
Bruce Momjian's avatar
Bruce Momjian committed
1009
 *	and store the user name at *ident_user. If it's anything else,
1010
 *	return false.
1011 1012 1013 1014 1015
 */
static bool
interpret_ident_response(char *ident_response,
						 char *ident_user)
{
1016 1017
	char	   *cursor = ident_response;		/* Cursor into
												 * *ident_response */
1018 1019 1020 1021 1022 1023

	/*
	 * Ident's response, in the telnet tradition, should end in crlf
	 * (\r\n).
	 */
	if (strlen(ident_response) < 2)
1024
		return false;
1025
	else if (ident_response[strlen(ident_response) - 2] != '\r')
1026
		return false;
1027 1028 1029 1030 1031 1032
	else
	{
		while (*cursor != ':' && *cursor != '\r')
			cursor++;			/* skip port field */

		if (*cursor != ':')
1033
			return false;
1034 1035 1036
		else
		{
			/* We're positioned to colon before response type field */
1037
			char		response_type[80];
1038
			int			i;		/* Index into *response_type */
1039 1040 1041 1042 1043

			cursor++;			/* Go over colon */
			while (isblank(*cursor))
				cursor++;		/* skip blanks */
			i = 0;
1044 1045
			while (*cursor != ':' && *cursor != '\r' && !isblank(*cursor) &&
				   i < (int) (sizeof(response_type) - 1))
1046 1047 1048 1049 1050
				response_type[i++] = *cursor++;
			response_type[i] = '\0';
			while (isblank(*cursor))
				cursor++;		/* skip blanks */
			if (strcmp(response_type, "USERID") != 0)
1051
				return false;
1052 1053 1054 1055 1056 1057 1058 1059
			else
			{
				/*
				 * It's a USERID response.  Good.  "cursor" should be
				 * pointing to the colon that precedes the operating
				 * system type.
				 */
				if (*cursor != ':')
1060
					return false;
1061 1062 1063 1064 1065 1066 1067
				else
				{
					cursor++;	/* Go over colon */
					/* Skip over operating system field. */
					while (*cursor != ':' && *cursor != '\r')
						cursor++;
					if (*cursor != ':')
1068
						return false;
1069 1070
					else
					{
1071
						int			i;	/* Index into *ident_user */
1072 1073 1074 1075

						cursor++;		/* Go over colon */
						while (isblank(*cursor))
							cursor++;	/* skip blanks */
Bruce Momjian's avatar
Bruce Momjian committed
1076
						/* Rest of line is user name.  Copy it over. */
1077 1078
						i = 0;
						while (*cursor != '\r' && i < IDENT_USERNAME_MAX)
1079 1080 1081
							ident_user[i++] = *cursor++;
						ident_user[i] = '\0';
						return true;
1082 1083 1084 1085 1086
					}
				}
			}
		}
	}
1087 1088 1089
}


1090
/*
1091 1092
 *	Talk to the ident server on host "remote_ip_addr" and find out who
 *	owns the tcp connection from his port "remote_port" to port
Bruce Momjian's avatar
Bruce Momjian committed
1093
 *	"local_port_addr" on host "local_ip_addr".	Return the user name the
1094
 *	ident server gives as "*ident_user".
1095
 *
1096
 *	IP addresses and port numbers are in network byte order.
1097
 *
1098
 *	But iff we're unable to get the information from ident, return false.
1099
 */
1100 1101 1102 1103 1104 1105
static bool
ident_inet(const struct in_addr remote_ip_addr,
		   const struct in_addr local_ip_addr,
		   const ushort remote_port,
		   const ushort local_port,
		   char *ident_user)
1106
{
Bruce Momjian's avatar
Bruce Momjian committed
1107 1108 1109 1110
	int			sock_fd,		/* File descriptor for socket on which we
								 * talk to Ident */
				rc;				/* Return code from a locally called
								 * function */
1111
	bool		ident_return;
1112 1113 1114 1115

	sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
	if (sock_fd == -1)
	{
1116
		elog(LOG, "Failed to create socket on which to talk to Ident server: %m");
1117
		ident_return = false;
1118 1119 1120 1121
	}
	else
	{
		struct sockaddr_in ident_server;
Bruce Momjian's avatar
Bruce Momjian committed
1122
		struct sockaddr_in la;
1123 1124 1125 1126 1127 1128 1129 1130

		/*
		 * Socket address of Ident server on the system from which client
		 * is attempting to connect to us.
		 */
		ident_server.sin_family = AF_INET;
		ident_server.sin_port = htons(IDENT_PORT);
		ident_server.sin_addr = remote_ip_addr;
Bruce Momjian's avatar
Bruce Momjian committed
1131 1132 1133

		/*
		 * Bind to the address which the client originally contacted,
Bruce Momjian's avatar
Bruce Momjian committed
1134 1135 1136
		 * otherwise the ident server won't be able to match up the right
		 * connection. This is necessary if the PostgreSQL server is
		 * running on an IP alias.
Bruce Momjian's avatar
Bruce Momjian committed
1137 1138 1139 1140
		 */
		memset(&la, 0, sizeof(la));
		la.sin_family = AF_INET;
		la.sin_addr = local_ip_addr;
Bruce Momjian's avatar
Bruce Momjian committed
1141
		rc = bind(sock_fd, (struct sockaddr *) & la, sizeof(la));
Bruce Momjian's avatar
Bruce Momjian committed
1142 1143 1144
		if (rc == 0)
		{
			rc = connect(sock_fd,
Bruce Momjian's avatar
Bruce Momjian committed
1145
			   (struct sockaddr *) & ident_server, sizeof(ident_server));
Bruce Momjian's avatar
Bruce Momjian committed
1146
		}
1147 1148
		if (rc != 0)
		{
1149
			/* save_errno is in case inet_ntoa changes errno */
Bruce Momjian's avatar
Bruce Momjian committed
1150
			int			save_errno = errno;
1151 1152 1153 1154 1155 1156

			elog(LOG, "Unable to connect to Ident server on the host which is "
				 "trying to connect to Postgres "
				 "(IP address %s, Port %d): %s",
				 inet_ntoa(remote_ip_addr), IDENT_PORT,
				 strerror(save_errno));
1157
			ident_return = false;
1158 1159 1160
		}
		else
		{
Bruce Momjian's avatar
Bruce Momjian committed
1161
			char		ident_query[80];
1162 1163

			/* The query we send to the Ident server */
Marc G. Fournier's avatar
Marc G. Fournier committed
1164
			snprintf(ident_query, 80, "%d,%d\n",
Bruce Momjian's avatar
Bruce Momjian committed
1165
					 ntohs(remote_port), ntohs(local_port));
1166
			/* loop in case send is interrupted */
Bruce Momjian's avatar
Bruce Momjian committed
1167 1168
			do
			{
1169 1170
				rc = send(sock_fd, ident_query, strlen(ident_query), 0);
			} while (rc < 0 && errno == EINTR);
1171 1172
			if (rc < 0)
			{
Bruce Momjian's avatar
Bruce Momjian committed
1173
				int			save_errno = errno;
1174 1175 1176 1177 1178 1179

				elog(LOG, "Unable to send query to Ident server on the host which is "
					 "trying to connect to Postgres (Host %s, Port %d), "
					 "even though we successfully connected to it: %s",
					 inet_ntoa(remote_ip_addr), IDENT_PORT,
					 strerror(save_errno));
1180
				ident_return = false;
1181 1182 1183
			}
			else
			{
Bruce Momjian's avatar
Bruce Momjian committed
1184
				char		ident_response[80 + IDENT_USERNAME_MAX];
1185

1186 1187
				rc = recv(sock_fd, ident_response,
						  sizeof(ident_response) - 1, 0);
1188 1189
				if (rc < 0)
				{
Bruce Momjian's avatar
Bruce Momjian committed
1190
					int			save_errno = errno;
1191 1192 1193

					elog(LOG, "Unable to receive response from Ident server "
						 "on the host which is "
Bruce Momjian's avatar
Bruce Momjian committed
1194
					 "trying to connect to Postgres (Host %s, Port %d), "
1195 1196 1197
						 "even though we successfully sent our query to it: %s",
						 inet_ntoa(remote_ip_addr), IDENT_PORT,
						 strerror(save_errno));
1198
					ident_return = false;
1199 1200 1201 1202
				}
				else
				{
					ident_response[rc] = '\0';
1203 1204
					ident_return = interpret_ident_response(ident_response,
															ident_user);
1205 1206 1207 1208 1209
				}
			}
			close(sock_fd);
		}
	}
1210
	return ident_return;
1211 1212
}

1213
/*
1214 1215
 *	Ask kernel about the credentials of the connecting process and
 *	determine the symbolic name of the corresponding user.
1216
 *
1217 1218
 *	Returns either true and the username put into "ident_user",
 *	or false if we were unable to determine the username.
1219 1220 1221 1222
 */
static bool
ident_unix(int sock, char *ident_user)
{
1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250
#if defined(HAVE_GETPEEREID)
	/* OpenBSD style:  */
	uid_t uid;
	gid_t gid;
	struct passwd *pass;

	errno = 0;
	if (getpeereid(sock,&uid,&gid) != 0)
	{
		/* We didn't get a valid credentials struct. */
		elog(LOG, "ident_unix: error receiving credentials: %m");
		return false;
	}

	pass = getpwuid(uid);

	if (pass == NULL)
	{
		elog(LOG, "ident_unix: unknown local user with uid %d",
			 (int) uid);
		return false;
	}

	StrNCpy(ident_user, pass->pw_name, IDENT_USERNAME_MAX + 1);

	return true;

#elsif defined(SO_PEERCRED)
1251
	/* Linux style: use getsockopt(SO_PEERCRED) */
1252
	struct ucred peercred;
1253
	ACCEPT_TYPE_ARG3 so_len = sizeof(peercred);
1254 1255 1256 1257 1258 1259 1260
	struct passwd *pass;

	errno = 0;
	if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &peercred, &so_len) != 0 ||
		so_len != sizeof(peercred))
	{
		/* We didn't get a valid credentials struct. */
1261
		elog(LOG, "ident_unix: error receiving credentials: %m");
1262 1263 1264 1265 1266 1267 1268
		return false;
	}

	pass = getpwuid(peercred.uid);

	if (pass == NULL)
	{
1269 1270
		elog(LOG, "ident_unix: unknown local user with uid %d",
			 (int) peercred.uid);
1271 1272 1273
		return false;
	}

1274
	StrNCpy(ident_user, pass->pw_name, IDENT_USERNAME_MAX + 1);
1275 1276

	return true;
1277

1278
#elif defined(HAVE_STRUCT_CMSGCRED) || defined(HAVE_STRUCT_FCRED) || (defined(HAVE_STRUCT_SOCKCRED) && defined(LOCAL_CREDS))
1279 1280 1281
	struct msghdr msg;

/* Credentials structure */
1282
#ifdef HAVE_STRUCT_CMSGCRED
1283
	typedef struct cmsgcred Cred;
1284

1285
#define cruid cmcred_uid
1286
#elif HAVE_STRUCT_FCRED
1287
	typedef struct fcred Cred;
1288

1289
#define cruid fc_uid
1290 1291
#elif HAVE_STRUCT_SOCKCRED
	typedef struct sockcred Cred;
1292

1293
#define cruid sc_uid
1294
#endif
1295
	Cred	   *cred;
1296 1297

	/* Compute size without padding */
1298 1299
	char		cmsgmem[ALIGN(sizeof(struct cmsghdr)) + ALIGN(sizeof(Cred))];	/* for NetBSD */

1300
	/* Point to start of first structure */
1301
	struct cmsghdr *cmsg = (struct cmsghdr *) cmsgmem;
1302 1303

	struct iovec iov;
1304
	char		buf;
1305 1306 1307 1308 1309
	struct passwd *pw;

	memset(&msg, 0, sizeof(msg));
	msg.msg_iov = &iov;
	msg.msg_iovlen = 1;
1310
	msg.msg_control = (char *) cmsg;
1311 1312 1313 1314
	msg.msg_controllen = sizeof(cmsgmem);
	memset(cmsg, 0, sizeof(cmsgmem));

	/*
1315 1316 1317
	 * The one character which is received here is not meaningful; its
	 * purposes is only to make sure that recvmsg() blocks long enough for
	 * the other side to send its credentials.
1318 1319 1320 1321 1322 1323 1324 1325
	 */
	iov.iov_base = &buf;
	iov.iov_len = 1;

	if (recvmsg(sock, &msg, 0) < 0 ||
		cmsg->cmsg_len < sizeof(cmsgmem) ||
		cmsg->cmsg_type != SCM_CREDS)
	{
1326
		elog(LOG, "ident_unix: error receiving credentials: %m");
1327 1328 1329
		return false;
	}

1330
	cred = (Cred *) CMSG_DATA(cmsg);
1331

1332
	pw = getpwuid(cred->cruid);
1333 1334
	if (pw == NULL)
	{
1335 1336
		elog(LOG, "ident_unix: unknown local user with uid %d",
			 (int) cred->cruid);
1337 1338 1339
		return false;
	}

1340
	StrNCpy(ident_user, pw->pw_name, IDENT_USERNAME_MAX + 1);
1341 1342 1343 1344

	return true;

#else
1345
	elog(LOG, "'ident' auth is not supported on local connections on this platform");
1346

1347
	return false;
1348
#endif
1349
}
1350

Bruce Momjian's avatar
Bruce Momjian committed
1351 1352


1353
/*
1354 1355 1356 1357
 *	Determine the username of the initiator of the connection described
 *	by "port".	Then look in the usermap file under the usermap
 *	port->auth_arg and see if that user is equivalent to Postgres user
 *	port->user.
1358
 *
1359
 *	Return STATUS_OK if yes, STATUS_ERROR if no match (or couldn't get info).
1360 1361
 */
int
1362
authident(hbaPort *port)
1363
{
1364
	char		ident_user[IDENT_USERNAME_MAX + 1];
1365

1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381
	switch (port->raddr.sa.sa_family)
	{
		case AF_INET:
			if (!ident_inet(port->raddr.in.sin_addr,
							port->laddr.in.sin_addr,
							port->raddr.in.sin_port,
							port->laddr.in.sin_port, ident_user))
				return STATUS_ERROR;
			break;
		case AF_UNIX:
			if (!ident_unix(port->sock, ident_user))
				return STATUS_ERROR;
			break;
		default:
			return STATUS_ERROR;
	}
1382

1383
	if (check_ident_usermap(port->auth_arg, port->user, ident_user))
1384
		return STATUS_OK;
1385
	else
1386
		return STATUS_ERROR;
1387 1388 1389
}


1390
/*
1391 1392 1393
 *	Determine what authentication method should be used when accessing database
 *	"database" from frontend "raddr", user "user".	Return the method and
 *	an optional argument (stored in fields of *port), and STATUS_OK.
1394
 *
1395 1396 1397
 *	Note that STATUS_ERROR indicates a problem with the hba config file.
 *	If the file is OK but does not contain any entry matching the request,
 *	we return STATUS_OK and method = uaReject.
1398
 */
1399
int
1400
hba_getauthmethod(hbaPort *port)
1401
{
1402 1403 1404
	if (check_hba(port))
		return STATUS_OK;
	else
1405
		return STATUS_ERROR;
1406
}