📄 hba.c
字号:
/*------------------------------------------------------------------------- * * 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-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $PostgreSQL: pgsql/src/backend/libpq/hba.c,v 1.149 2005/10/17 16:24:19 tgl Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include <ctype.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 "libpq/crypt.h"#include "libpq/libpq.h"#include "miscadmin.h"#include "nodes/pg_list.h"#include "storage/fd.h"#include "utils/flatfiles.h"#include "utils/guc.h"#define atooid(x) ((Oid) strtoul((x), NULL, 10))#define atoxid(x) ((TransactionId) strtoul((x), NULL, 10))/* Max size of username ident server can return */#define IDENT_USERNAME_MAX 512/* Standard TCP port number for Ident service. Assigned by IANA */#define IDENT_PORT 113/* This is used to separate values in multi-valued column strings */#define MULTI_VALUE_SEP "\001"#define MAX_TOKEN 256/* * These variables hold the pre-parsed contents of the hba and ident * configuration files, as well as the flat auth file. * 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. *//* pre-parsed content of HBA config file and corresponding line #s */static List *hba_lines = NIL;static List *hba_line_nums = NIL;/* pre-parsed content of ident usermap file and corresponding line #s */static List *ident_lines = NIL;static List *ident_line_nums = NIL;/* pre-parsed content of flat auth file and corresponding line #s */static List *role_lines = NIL;static List *role_line_nums = NIL;/* sorted entries so we can do binary search lookups */static List **role_sorted = NULL; /* sorted role list, for bsearch() */static int role_length;static void tokenize_file(const char *filename, FILE *file, List **lines, List **line_nums);static char *tokenize_inc_file(const char *outer_filename, 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, commas, beginning of line, and * end of line. Blank means space or tab. Tokens can be delimited by * double quotes (and usually are, in current usage). * * The token, if any, is returned at *buf (a buffer of size bufsz). * * If successful: store null-terminated token at *buf and return TRUE. * If no more tokens on line: set *buf = '\0' and return FALSE. * * Leave file positioned at the character immediately after the token or EOF, * whichever comes first. If no more tokens on line, position the file to the * beginning of the next line or EOF, whichever comes first. * * Handle comments. Treat unquoted keywords that might be role names or * database names specially, by appending a newline to them. */static boolnext_token(FILE *fp, char *buf, int bufsz){ int c; char *start_buf = buf; char *end_buf = buf + (bufsz - 2); bool in_quote = false; bool was_quote = false; bool saw_quote = false; Assert(end_buf > start_buf); /* Move over initial whitespace and commas */ while ((c = getc(fp)) != EOF && (pg_isblank(c) || c == ',')) ; if (c == EOF || c == '\n') { *buf = '\0'; return false; } /* * 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; saw_quote = true; } 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'; if (!saw_quote && (strcmp(start_buf, "all") == 0 || strcmp(start_buf, "sameuser") == 0 || strcmp(start_buf, "samegroup") == 0 || strcmp(start_buf, "samerole") == 0)) { /* append newline to a magical keyword */ *buf++ = '\n'; *buf = '\0'; } return (saw_quote || buf > start_buf);}/* * 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 a palloc'd string, or NULL if we have reached EOL. */static char *next_token_expand(const char *filename, FILE *file){ char buf[MAX_TOKEN]; char *comma_str = pstrdup(""); bool got_something = false; bool trailing_comma; char *incbuf; int needed; do { if (!next_token(file, buf, sizeof(buf))) break; got_something = true; if (strlen(buf) > 0 && 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(filename, buf + 1); else incbuf = pstrdup(buf); needed = strlen(comma_str) + strlen(incbuf) + 1; if (trailing_comma) needed++; comma_str = repalloc(comma_str, needed); strcat(comma_str, incbuf); if (trailing_comma) strcat(comma_str, MULTI_VALUE_SEP); pfree(incbuf); } while (trailing_comma); if (!got_something) { pfree(comma_str); return NULL; } return comma_str;}/* * Free memory used by lines/tokens (i.e., structure built by tokenize_file) */static voidfree_lines(List **lines, List **line_nums){ /* * Either both must be non-NULL, or both must be NULL */ Assert((*lines != NIL && *line_nums != NIL) || (*lines == NIL && *line_nums == NIL)); if (*lines) { /* * "lines" is a list of lists; each of those sublists consists of * palloc'ed tokens, so we want to free each pointed-to token in a * sublist, followed by the sublist itself, and finally the whole * list. */ ListCell *line; foreach(line, *lines) { List *ln = lfirst(line); ListCell *token; foreach(token, ln) pfree(lfirst(token)); /* free the sublist structure itself */ list_free(ln); } /* free the list structure itself */ list_free(*lines); /* clear the static variable */ *lines = NIL; } if (*line_nums) { list_free(*line_nums); *line_nums = NIL; }}static char *tokenize_inc_file(const char *outer_filename, const char *inc_filename){ char *inc_fullname; FILE *inc_file; List *inc_lines; List *inc_line_nums; ListCell *line; char *comma_str; if (is_absolute_path(inc_filename)) { /* absolute path is taken as-is */ inc_fullname = pstrdup(inc_filename); } else { /* relative path is relative to dir of calling file */ inc_fullname = (char *) palloc(strlen(outer_filename) + 1 + strlen(inc_filename) + 1); strcpy(inc_fullname, outer_filename); get_parent_directory(inc_fullname); join_path_components(inc_fullname, inc_fullname, inc_filename); canonicalize_path(inc_fullname); } inc_file = AllocateFile(inc_fullname, "r"); if (inc_file == NULL) { 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 single space, it matches nothing */ return pstrdup(" "); } /* There is possible recursion here if the file contains @ */ tokenize_file(inc_fullname, inc_file, &inc_lines, &inc_line_nums); FreeFile(inc_file); pfree(inc_fullname); /* Create comma-separated string from List */ comma_str = pstrdup(""); foreach(line, inc_lines) { List *token_list = (List *) lfirst(line); ListCell *token; foreach(token, token_list) { int oldlen = strlen(comma_str); int needed; needed = oldlen + strlen(lfirst(token)) + 1; if (oldlen > 0) needed++; comma_str = repalloc(comma_str, needed); if (oldlen > 0) strcat(comma_str, MULTI_VALUE_SEP); strcat(comma_str, lfirst(token)); } } free_lines(&inc_lines, &inc_line_nums); /* if file is empty, return single space rather than empty string */ if (strlen(comma_str) == 0) { pfree(comma_str); return pstrdup(" "); } return comma_str;}/* * Tokenize the given file, storing the resulting data into two lists: * a list of sublists, each sublist containing the tokens in a line of * the file, and a list of line numbers. * * filename must be the absolute path to the target file. */static voidtokenize_file(const char *filename, FILE *file, List **lines, List **line_nums){ List *current_line = NIL; int line_number = 1; char *buf; *lines = *line_nums = NIL; while (!feof(file)) { buf = next_token_expand(filename, file); /* add token to list, unless we are at EOL or comment start */ if (buf) { if (current_line == NIL) { /* make a new line List, record its line number */ current_line = lappend(current_line, buf); *lines = lappend(*lines, current_line); *line_nums = lappend_int(*line_nums, line_number); } else { /* append token to current line's list */ current_line = lappend(current_line, buf); } } else { /* we are at real or logical EOL, so force a new line List */ current_line = NIL; /* Advance line number whenever we reach EOL */ line_number++; } }}/* * Compare two lines based on their role/member names. * * Used for bsearch() lookup. */static introle_bsearch_cmp(const void *role, const void *list){ char *role2 = linitial(*(List **) list); return strcmp(role, role2);}/* * Lookup a role name in the pg_auth file */List **get_role_line(const char *role){ /* On some versions of Solaris, bsearch of zero items dumps core */ if (role_length == 0) return NULL; return (List **) bsearch((void *) role, (void *) role_sorted, role_length, sizeof(List *), role_bsearch_cmp);}/* * Does user belong to role? * * user is always the name given as the attempted login identifier. * We check to see if it is a member of the specified role name. */static boolis_member(const char *user, const char *role){ List **line; ListCell *line_item; if ((line = get_role_line(user)) == NULL) return false; /* if user not exist, say "no" */ /* A user always belongs to its own role */ if (strcmp(user, role) == 0) return true; /* * skip over the role name, password, valuntil, examine all the membership * entries */ if (list_length(*line) < 4) return false; for_each_cell(line_item, lnext(lnext(lnext(list_head(*line))))) { if (strcmp((char *) lfirst(line_item), role) == 0) return true; } return false;}/* * Check comma-separated list for a match to role, allowing group names. * * NB: param_str is destructively modified! In current usage, this is * okay only because this code is run after forking off from the postmaster, * and so it doesn't matter that we clobber the stored hba info. */static boolcheck_role(const char *role, 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 (is_member(role, tok + 1)) return true; } else if (strcmp(tok, role) == 0 || strcmp(tok, "all\n") == 0) return true; } return false;}/* * Check to see if db/role combination matches param string. * * NB: param_str is destructively modified! In current usage, this is * okay only because this code is run after forking off from the postmaster, * and so it doesn't matter that we clobber the stored hba info. */static bool
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -