diff options
Diffstat (limited to 'src/backend/libpq/hba.c')
-rw-r--r-- | src/backend/libpq/hba.c | 1373 |
1 files changed, 0 insertions, 1373 deletions
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c deleted file mode 100644 index 4ac150572c2..00000000000 --- a/src/backend/libpq/hba.c +++ /dev/null @@ -1,1373 +0,0 @@ -/*------------------------------------------------------------------------- - * - * 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-2002, 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.84 2002/06/20 20:29:28 momjian Exp $ - * - *------------------------------------------------------------------------- - */ -#include "postgres.h" - -#include <errno.h> -#include <pwd.h> -#include <fcntl.h> -#include <sys/types.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); - -/* - * Some standard C libraries, including GNU, have an isblank() function. - * Others, including Solaris, do not. So we have our own. - */ -static bool -isblank(const char c) -{ - return c == ' ' || c == '\t'; -} - - -/* - * 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. - */ -void -next_token(FILE *fp, char *buf, const int bufsz) -{ - int c; - 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 && (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' && - (!isblank(c) || in_quote == true)) - { - /* skip comments to EOL */ - if (c == '#' && !in_quote) - { - while ((c = getc(fp)) != EOF && c != '\n') - ; - continue; - } - - 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)) - *buf++ = c; - - /* 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 */ - 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. - */ -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 void -free_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) - { - 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, - 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; - } - - /* 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 int -user_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 int -user_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) -{ - 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) -{ - return (List **) bsearch((void *) user, - (void *) user_sorted, - user_length, - sizeof(List *), - user_group_bsearch_cmp); -} - - -/* - * Check group for a specific user. - */ -static int -check_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 1; - } - - return 0; -} - -/* - * Check comma user list for a specific user, handle group names. - */ -static int -check_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 1; - } - else if (strcmp(tok, user) == 0 || - strcmp(tok, "all") == 0) - return 1; - } - - return 0; -} - -/* - * Check to see if db/user combination matches param string. - */ -static int -check_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 1; - else if (strcmp(tok, "sameuser") == 0) - { - if (strcmp(dbname, user) == 0) - return 1; - } - else if (strcmp(tok, "samegroup") == 0) - { - if (check_group(dbname, user)) - return 1; - } - else if (strcmp(tok, dbname) == 0) - return 1; - } - return 0; -} - - -/* - * 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. - */ -static void -parse_hba_auth(List *line, UserAuth *userauth_p, char *auth_arg, - bool *error_p) -{ - char *token; - - 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 - else - *error_p = true; - line = lnext(line); - } - - if (!*error_p) - { - /* Get the authentication argument token, if any */ - if (!line) - auth_arg[0] = '\0'; - else - { - StrNCpy(auth_arg, lfirst(line), MAX_AUTH_ARG - 1); - /* If there is more on the line, it is an error */ - if (lnext(line)) - *error_p = true; - } - } -} - - -/* - * Process one line from the hba config file. - * - * 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, - * return *error_p true, after issuing a message to the log. If no error, - * leave *error_p as it was. - */ -static void -parse_hba(List *line, hbaPort *port, bool *found_p, bool *error_p) -{ - int line_number; - char *token; - char *db; - char *user; - - Assert(line != NIL); - line_number = lfirsti(line); - line = lnext(line); - Assert(line != NIL); - /* Check the record type. */ - token = lfirst(line); - if (strcmp(token, "local") == 0) - { - /* Get the database. */ - line = lnext(line); - if (!line) - goto hba_syntax; - db = lfirst(line); - - /* Get the user. */ - line = lnext(line); - if (!line) - goto hba_syntax; - user = lfirst(line); - - line = lnext(line); - if (!line) - goto hba_syntax; - - /* Read the rest of the line. */ - parse_hba_auth(line, &port->auth_method, port->auth_arg, error_p); - if (*error_p) - goto hba_syntax; - - /* - * Disallow auth methods that always need AF_INET sockets to work. - */ - if (port->auth_method == uaKrb4 || - port->auth_method == uaKrb5) - goto hba_syntax; - - if (port->raddr.sa.sa_family != AF_UNIX) - return; - } - else if (strcmp(token, "host") == 0 || strcmp(token, "hostssl") == 0) - { - struct in_addr file_ip_addr, - mask; - - if (strcmp(token, "hostssl") == 0) - { -#ifdef USE_SSL - /* Record does not match if we are not on an SSL connection */ - if (!port->ssl) - return; - - /* Placeholder to require specific SSL level, perhaps? */ - /* Or a client certificate */ - - /* Since we were on SSL, proceed as with normal 'host' mode */ -#else - /* We don't accept this keyword at all if no SSL support */ - goto hba_syntax; -#endif - } - - /* Get the database. */ - line = lnext(line); - if (!line) - goto hba_syntax; - db = lfirst(line); - - /* Get the user. */ - line = lnext(line); - if (!line) - goto hba_syntax; - user = lfirst(line); - - /* Read the IP address field. */ - line = lnext(line); - if (!line) - goto hba_syntax; - token = lfirst(line); - if (!inet_aton(token, &file_ip_addr)) - goto hba_syntax; - - /* Read the mask field. */ - line = lnext(line); - if (!line) - goto hba_syntax; - token = lfirst(line); - if (!inet_aton(token, &mask)) - goto hba_syntax; - - /* Read the rest of the line. */ - line = lnext(line); - if (!line) - goto hba_syntax; - parse_hba_auth(line, &port->auth_method, port->auth_arg, error_p); - if (*error_p) - goto hba_syntax; - - /* Must meet network restrictions */ - if (port->raddr.sa.sa_family != AF_INET || - ((file_ip_addr.s_addr ^ port->raddr.in.sin_addr.s_addr) & mask.s_addr) != 0) - return; - } - else - goto hba_syntax; - - if (!check_db(port->database, port->user, db)) - return; - if (!check_user(port->user, user)) - return; - - /* Success */ - *found_p = true; - return; - -hba_syntax: - 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)"); - - *error_p = true; - return; -} - - -/* - * Scan the (pre-parsed) hba file line by line, looking for a match - * to the port's connection request. - */ -static bool -check_hba(hbaPort *port) -{ - bool found_entry = false; - bool error = false; - List *line; - - foreach(line, hba_lines) - { - parse_hba(lfirst(line), port, &found_entry, &error); - if (found_entry || error) - break; - } - - if (!error) - { - /* If no matching entry was found, synthesize 'reject' entry. */ - if (!found_entry) - port->auth_method = uaReject; - return true; - } - else - return false; -} - - - -/* - * 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; - List *line; - - 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) - { - int i = 0; - - group_sorted = palloc(group_length * sizeof(List *)); - - foreach(line, group_lines) - group_sorted[i++] = lfirst(line); - - qsort((void *) group_sorted, group_length, sizeof(List *), user_group_qsort_cmp); - } - 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) - { - int i = 0; - - user_sorted = palloc(user_length * sizeof(List *)); - - foreach(line, user_lines) - user_sorted[i++] = lfirst(line); - - qsort((void *) user_sorted, user_length, sizeof(List *), user_group_qsort_cmp); - } - else - user_sorted = NULL; -} - - -/* - * Read the config file and create a List of Lists of tokens in the file. - * If we find a file by the old name of the config file (pg_hba), we issue - * an error message because it probably needs to be converted. He didn't - * follow directions and just installed his old hba file in the new database - * system. - */ -void -load_hba(void) -{ - int bufsize; - FILE *file; /* The config file we have to read */ - char *conf_file; /* The name of the config file */ - - if (hba_lines) - free_lines(&hba_lines); - - /* 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); - - file = AllocateFile(conf_file, "r"); - if (file == NULL) - { - /* The open of the config file failed. */ - elog(LOG, "load_hba: Unable to open authentication config file \"%s\": %m", - conf_file); - pfree(conf_file); - } - else - { - hba_lines = tokenize_file(file); - FreeFile(file); - } - pfree(conf_file); -} - - -/* - * Process one line from the ident config file. - * - * Take the line and compare it to the needed map, pg_user and ident_user. - * *found_p and *error_p are set according to our results. - */ -static void -parse_ident_usermap(List *line, const char *usermap_name, const char *pg_user, - const char *ident_user, bool *found_p, bool *error_p) -{ - int line_number; - char *token; - char *file_map; - char *file_pguser; - char *file_ident_user; - - *found_p = false; - *error_p = false; - - Assert(line != NIL); - line_number = lfirsti(line); - line = lnext(line); - Assert(line != NIL); - - /* Get the map token (must exist) */ - token = lfirst(line); - file_map = token; - - /* Get the ident user token (must be provided) */ - line = lnext(line); - if (!line) - goto ident_syntax; - token = lfirst(line); - file_ident_user = token; - - /* 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; - return; - -ident_syntax: - 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)"); - - *error_p = true; - return; -} - - -/* - * Scan the (pre-parsed) ident usermap file line by line, looking for a match - * - * See if the user with ident username "ident_user" is allowed to act - * as Postgres user "pguser" according to usermap "usermap_name". - * - * 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. - * - * Iff authorized, return true. - */ -static bool -check_ident_usermap(const char *usermap_name, - const char *pg_user, - const char *ident_user) -{ - List *line; - bool found_entry = false, - error = false; - - if (usermap_name[0] == '\0') - { - elog(LOG, "check_ident_usermap: hba configuration file does not " - "have the usermap field filled in in the entry that pertains " - "to this connection. That field is essential for Ident-based " - "authentication."); - 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; -} - - -/* - * Read the ident config file and create a List of Lists of tokens in the file. - */ -void -load_ident(void) -{ - FILE *file; /* The map file we have to read */ - char *map_file; /* The name of the map file we have to - * read */ - int bufsize; - - 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); - - file = AllocateFile(map_file, "r"); - if (file == NULL) - { - /* The open of the map file failed. */ - elog(LOG, "load_ident: Unable to open usermap file \"%s\": %m", - map_file); - } - else - { - ident_lines = tokenize_file(file); - FreeFile(file); - } - pfree(map_file); -} - - -/* - * Parse the string "*ident_response" as a response from a query to an Ident - * server. If it's a normal response indicating a user name, return true - * and store the user name at *ident_user. If it's anything else, - * return false. - */ -static bool -interpret_ident_response(char *ident_response, - char *ident_user) -{ - char *cursor = ident_response; /* Cursor into - * *ident_response */ - - /* - * Ident's response, in the telnet tradition, should end in crlf - * (\r\n). - */ - if (strlen(ident_response) < 2) - return false; - else if (ident_response[strlen(ident_response) - 2] != '\r') - return false; - else - { - while (*cursor != ':' && *cursor != '\r') - cursor++; /* skip port field */ - - if (*cursor != ':') - return false; - else - { - /* We're positioned to colon before response type field */ - char response_type[80]; - int i; /* Index into *response_type */ - - cursor++; /* Go over colon */ - while (isblank(*cursor)) - cursor++; /* skip blanks */ - i = 0; - while (*cursor != ':' && *cursor != '\r' && !isblank(*cursor) && - i < (int) (sizeof(response_type) - 1)) - response_type[i++] = *cursor++; - response_type[i] = '\0'; - while (isblank(*cursor)) - cursor++; /* skip blanks */ - if (strcmp(response_type, "USERID") != 0) - return false; - else - { - /* - * It's a USERID response. Good. "cursor" should be - * pointing to the colon that precedes the operating - * system type. - */ - if (*cursor != ':') - return false; - else - { - cursor++; /* Go over colon */ - /* Skip over operating system field. */ - while (*cursor != ':' && *cursor != '\r') - cursor++; - if (*cursor != ':') - return false; - else - { - int i; /* Index into *ident_user */ - - cursor++; /* Go over colon */ - while (isblank(*cursor)) - cursor++; /* skip blanks */ - /* Rest of line is user name. Copy it over. */ - i = 0; - while (*cursor != '\r' && i < IDENT_USERNAME_MAX) - ident_user[i++] = *cursor++; - ident_user[i] = '\0'; - return true; - } - } - } - } - } -} - - -/* - * 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 - * "local_port_addr" on host "local_ip_addr". Return the user name the - * ident server gives as "*ident_user". - * - * IP addresses and port numbers are in network byte order. - * - * But iff we're unable to get the information from ident, return false. - */ -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) -{ - int sock_fd, /* File descriptor for socket on which we - * talk to Ident */ - rc; /* Return code from a locally called - * function */ - bool ident_return; - - sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_IP); - if (sock_fd == -1) - { - elog(LOG, "Failed to create socket on which to talk to Ident server: %m"); - ident_return = false; - } - else - { - struct sockaddr_in ident_server; - struct sockaddr_in la; - - /* - * 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; - - /* - * Bind to the address which the client originally contacted, - * 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. - */ - memset(&la, 0, sizeof(la)); - la.sin_family = AF_INET; - la.sin_addr = local_ip_addr; - rc = bind(sock_fd, (struct sockaddr *) & la, sizeof(la)); - if (rc == 0) - { - rc = connect(sock_fd, - (struct sockaddr *) & ident_server, sizeof(ident_server)); - } - if (rc != 0) - { - /* save_errno is in case inet_ntoa changes errno */ - int save_errno = errno; - - 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)); - ident_return = false; - } - else - { - char ident_query[80]; - - /* The query we send to the Ident server */ - snprintf(ident_query, 80, "%d,%d\n", - ntohs(remote_port), ntohs(local_port)); - /* loop in case send is interrupted */ - do { - rc = send(sock_fd, ident_query, strlen(ident_query), 0); - } while (rc < 0 && errno == EINTR); - if (rc < 0) - { - int save_errno = errno; - - 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)); - ident_return = false; - } - else - { - char ident_response[80 + IDENT_USERNAME_MAX]; - - rc = recv(sock_fd, ident_response, - sizeof(ident_response) - 1, 0); - if (rc < 0) - { - int save_errno = errno; - - elog(LOG, "Unable to receive response from Ident server " - "on the host which is " - "trying to connect to Postgres (Host %s, Port %d), " - "even though we successfully sent our query to it: %s", - inet_ntoa(remote_ip_addr), IDENT_PORT, - strerror(save_errno)); - ident_return = false; - } - else - { - ident_response[rc] = '\0'; - ident_return = interpret_ident_response(ident_response, - ident_user); - } - } - close(sock_fd); - } - } - return ident_return; -} - -/* - * Ask kernel about the credentials of the connecting process and - * determine the symbolic name of the corresponding user. - * - * Returns either true and the username put into "ident_user", - * or false if we were unable to determine the username. - */ -static bool -ident_unix(int sock, char *ident_user) -{ -#if defined(SO_PEERCRED) - /* Linux style: use getsockopt(SO_PEERCRED) */ - struct ucred peercred; - ACCEPT_TYPE_ARG3 so_len = sizeof(peercred); - 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. */ - elog(LOG, "ident_unix: error receiving credentials: %m"); - return false; - } - - pass = getpwuid(peercred.uid); - - if (pass == NULL) - { - elog(LOG, "ident_unix: unknown local user with uid %d", - (int) peercred.uid); - return false; - } - - StrNCpy(ident_user, pass->pw_name, IDENT_USERNAME_MAX + 1); - - return true; - -#elif defined(HAVE_STRUCT_CMSGCRED) || defined(HAVE_STRUCT_FCRED) || (defined(HAVE_STRUCT_SOCKCRED) && defined(LOCAL_CREDS)) - struct msghdr msg; - -/* Credentials structure */ -#ifdef HAVE_STRUCT_CMSGCRED - typedef struct cmsgcred Cred; - -#define cruid cmcred_uid -#elif HAVE_STRUCT_FCRED - typedef struct fcred Cred; - -#define cruid fc_uid -#elif HAVE_STRUCT_SOCKCRED - typedef struct sockcred Cred; - -#define cruid sc_uid -#endif - Cred *cred; - - /* Compute size without padding */ - char cmsgmem[ALIGN(sizeof(struct cmsghdr)) + ALIGN(sizeof(Cred))]; /* for NetBSD */ - - /* Point to start of first structure */ - struct cmsghdr *cmsg = (struct cmsghdr *) cmsgmem; - - struct iovec iov; - char buf; - struct passwd *pw; - - memset(&msg, 0, sizeof(msg)); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - msg.msg_control = (char *) cmsg; - msg.msg_controllen = sizeof(cmsgmem); - memset(cmsg, 0, sizeof(cmsgmem)); - - /* - * 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. - */ - iov.iov_base = &buf; - iov.iov_len = 1; - - if (recvmsg(sock, &msg, 0) < 0 || - cmsg->cmsg_len < sizeof(cmsgmem) || - cmsg->cmsg_type != SCM_CREDS) - { - elog(LOG, "ident_unix: error receiving credentials: %m"); - return false; - } - - cred = (Cred *) CMSG_DATA(cmsg); - - pw = getpwuid(cred->cruid); - if (pw == NULL) - { - elog(LOG, "ident_unix: unknown local user with uid %d", - (int) cred->cruid); - return false; - } - - StrNCpy(ident_user, pw->pw_name, IDENT_USERNAME_MAX + 1); - - return true; - -#else - elog(LOG, "'ident' auth is not supported on local connections on this platform"); - - return false; -#endif -} - - - -/* - * 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. - * - * Return STATUS_OK if yes, STATUS_ERROR if no match (or couldn't get info). - */ -int -authident(hbaPort *port) -{ - char ident_user[IDENT_USERNAME_MAX + 1]; - - 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; - } - - if (check_ident_usermap(port->auth_arg, port->user, ident_user)) - return STATUS_OK; - else - return STATUS_ERROR; -} - - -/* - * 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. - * - * 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. - */ -int -hba_getauthmethod(hbaPort *port) -{ - if (check_hba(port)) - return STATUS_OK; - else - return STATUS_ERROR; -} - |