hba.c

来自「PostgreSQL7.4.6 for Linux」· C语言 代码 · 共 1,584 行 · 第 1/3 页

C
1,584
字号
/*------------------------------------------------------------------------- * * hba.c *	  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). * * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION *	  $Header: /cvsroot/pgsql/src/backend/libpq/hba.c,v 1.116.2.3 2004/09/18 01:23:12 tgl Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include <errno.h>#include <pwd.h>#include <fcntl.h>#include <sys/param.h>#include <sys/socket.h>#if defined(HAVE_STRUCT_CMSGCRED) || defined(HAVE_STRUCT_FCRED) || defined(HAVE_STRUCT_SOCKCRED)#include <sys/uio.h>#include <sys/ucred.h>#endif#include <netinet/in.h>#include <arpa/inet.h>#include <unistd.h>#include "commands/user.h"#include "libpq/crypt.h"#include "libpq/libpq.h"#include "miscadmin.h"#include "nodes/pg_list.h"#include "storage/fd.h"#define IDENT_USERNAME_MAX 512/* Max size of username ident server can return *//* This is used to separate values in multi-valued column strings */#define MULTI_VALUE_SEP "\001"/* * These variables hold the pre-parsed contents of the hba and ident * configuration files.  Each is a list of sublists, one sublist for * each (non-empty, non-comment) line of the file.	Each sublist's * 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 */static List *ident_lines = NIL; /* pre-parsed contents of ident file */static List *group_lines = NIL; /* pre-parsed contents of group file */static List *user_lines = NIL;	/* pre-parsed contents of user password								 * file *//* sorted entries so we can do binary search lookups */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;static List *tokenize_file(FILE *file);static char *tokenize_inc_file(const char *inc_filename);/* * isblank() exists in the ISO C99 spec, but it's not very portable yet, * so provide our own version. */static boolpg_isblank(const char c){	return c == ' ' || c == '\t' || c == '\r';}/* *	 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. */voidnext_token(FILE *fp, char *buf, const int bufsz){	int			c;	char	   *start_buf = buf;	char	   *end_buf = buf + (bufsz - 1);	bool		in_quote = false;	bool		was_quote = false;	/* Move over initial whitespace and commas */	while ((c = getc(fp)) != EOF && (pg_isblank(c) || c == ','))		;	if (c != EOF && c != '\n')	{		/*		 * Build a token in buf of next characters up to EOF, EOL,		 * unquoted comma, or unquoted whitespace.		 */		while (c != EOF && c != '\n' &&			   (!pg_isblank(c) || in_quote == true))		{			/* skip comments to EOL */			if (c == '#' && !in_quote)			{				while ((c = getc(fp)) != EOF && c != '\n')					;				/* If only comment, consume EOL too; return EOL */				if (c != EOF && buf == start_buf)					c = getc(fp);				break;			}			if (buf >= end_buf)			{				*buf = '\0';				ereport(LOG,						(errcode(ERRCODE_CONFIG_FILE_ERROR),						 errmsg("authentication file token too long, skipping: \"%s\"",								start_buf)));				/* Discard remainder of line */				while ((c = getc(fp)) != EOF && c != '\n')					;				break;			}			if (c != '"' || (c == '"' && was_quote))				*buf++ = c;			/* We pass back the comma so the caller knows there is more */			if ((pg_isblank(c) || c == ',') && !in_quote)				break;			/* Literal double-quote is two double-quotes */			if (in_quote && c == '"')				was_quote = !was_quote;			else				was_quote = false;			if (c == '"')				in_quote = !in_quote;			c = getc(fp);		}		/*		 * Put back the char right after the token (critical in case it is		 * EOL, since we need to detect end-of-line at next call).		 */		if (c != EOF)			ungetc(c, fp);	}	*buf = '\0';}/* *	 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. * * The result is always a palloc'd string.  If it's zero-length then * we have reached EOL. */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;		if (buf[strlen(buf) - 1] == ',')		{			trailing_comma = true;			buf[strlen(buf) - 1] = '\0';		}		else			trailing_comma = false;		/* Is this referencing a file? */		if (buf[0] == '@')			incbuf = tokenize_inc_file(buf + 1);		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;}/* * Free memory used by lines/tokens (i.e., structure built by tokenize_file) */static voidfree_lines(List **lines){	if (*lines)	{		List	   *line,				   *token;		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;	List	   *inc_lines;	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)	{		ereport(LOG,				(errcode_for_file_access(),				 errmsg("could not 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,						  strlen(comma_str) + strlen(lfirst(token)) + 1);			strcat(comma_str, lfirst(token));		}	}	free_lines(&inc_lines);	return comma_str;}/* *	Read the given file and create a list of line sublists. */static List *tokenize_file(FILE *file){	List	   *lines = NIL;	List	   *next_line = NIL;	int			line_number = 1;	char	   *buf;	while (!feof(file))	{		buf = next_token_expand(file);		/* add token to list, unless we are at EOL or comment start */		if (buf[0] != '\0')		{			if (next_line == NIL)			{				/* make a new line List */				next_line = makeListi1(line_number);				lines = lappend(lines, next_line);			}			/* append token to current line's list */			next_line = lappend(next_line, buf);		}		else		{			/* we are at real or logical EOL, so force a new line List */			next_line = NIL;			/* Don't forget to pfree the next_token_expand result */			pfree(buf);		}		/* Advance line number whenever we reach EOL */		if (next_line == NIL)			line_number++;	}	return lines;}/* * Compare two lines based on their user/group names. * * Used for qsort() sorting. */static intuser_group_qsort_cmp(const void *list1, const void *list2){	/* first node is line number */	char	   *user1 = lfirst(lnext(*(List **) list1));	char	   *user2 = lfirst(lnext(*(List **) list2));	return strcmp(user1, user2);}/* * Compare two lines based on their user/group names. * * Used for bsearch() lookup. */static intuser_group_bsearch_cmp(const void *user, const void *list){	/* first node is line number */	char	   *user2 = lfirst(lnext(*(List **) list));	return strcmp(user, user2);}/* * Lookup a group name in the pg_group file */static List **get_group_line(const char *group){	/* On some versions of Solaris, bsearch of zero items dumps core */	if (group_length == 0)		return NULL;	return (List **) bsearch((void *) group,							 (void *) group_sorted,							 group_length,							 sizeof(List *),							 user_group_bsearch_cmp);}/* * Lookup a user name in the pg_shadow file */List **get_user_line(const char *user){	/* On some versions of Solaris, bsearch of zero items dumps core */	if (user_length == 0)		return NULL;	return (List **) bsearch((void *) user,							 (void *) user_sorted,							 user_length,							 sizeof(List *),							 user_group_bsearch_cmp);}/* * Check group for a specific user. */static boolcheck_group(char *group, char *user){	List	  **line,			   *l;	if ((line = get_group_line(group)) != NULL)	{		foreach(l, lnext(lnext(*line)))			if (strcmp(lfirst(l), user) == 0)			return true;	}	return false;}/* * Check comma user list for a specific user, handle group names. */static boolcheck_user(char *user, char *param_str){	char	   *tok;	for (tok = strtok(param_str, MULTI_VALUE_SEP); tok != NULL; tok = strtok(NULL, MULTI_VALUE_SEP))	{		if (tok[0] == '+')		{			if (check_group(tok + 1, user))				return true;		}		else if (strcmp(tok, user) == 0 ||				 strcmp(tok, "all") == 0)			return true;	}	return false;}/* * Check to see if db/user combination matches param string. */static boolcheck_db(char *dbname, char *user, char *param_str){	char	   *tok;	for (tok = strtok(param_str, MULTI_VALUE_SEP); tok != NULL; tok = strtok(NULL, MULTI_VALUE_SEP))	{		if (strcmp(tok, "all") == 0)			return true;		else if (strcmp(tok, "sameuser") == 0)		{			if (strcmp(dbname, user) == 0)				return true;		}		else if (strcmp(tok, "samegroup") == 0)		{			if (check_group(dbname, user))				return true;		}		else if (strcmp(tok, dbname) == 0)			return true;	}	return false;}/* *	Scan the rest of a host record (after the mask field) *	and return the interpretation of it as *userauth_p, *auth_arg_p, and *	*error_p.  line points to the next token of the line. */static voidparse_hba_auth(List *line, UserAuth *userauth_p, char **auth_arg_p,			   bool *error_p){	char	   *token;	*auth_arg_p = NULL;	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;		else if (strcmp(token, "md5") == 0)			*userauth_p = uaMD5;		else if (strcmp(token, "crypt") == 0)			*userauth_p = uaCrypt;#ifdef USE_PAM		else if (strcmp(token, "pam") == 0)			*userauth_p = uaPAM;#endif

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?