From 462b7d46d1feaf6a43a77bc8470a387316c02997 Mon Sep 17 00:00:00 2001 From: Bruce Momjian Date: Mon, 30 Jul 2001 14:50:24 +0000 Subject: [PATCH] Load pg_hba.conf and pg_ident.conf on startup and SIGHUP into List of Lists, and use that for user validation. Bruce Momjian --- src/backend/libpq/hba.c | 970 ++++++++++++++-------------- src/backend/postmaster/postmaster.c | 20 +- src/backend/tcop/postgres.c | 25 +- src/include/libpq/hba.h | 5 +- 4 files changed, 507 insertions(+), 513 deletions(-) diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c index 7c847ae354..9124137618 100644 --- a/src/backend/libpq/hba.c +++ b/src/backend/libpq/hba.c @@ -5,7 +5,7 @@ * wherein you authenticate a user by seeing what IP address the system * says he comes from and possibly using ident). * - * $Id: hba.c,v 1.55 2001/02/10 02:31:26 tgl Exp $ + * $Id: hba.c,v 1.56 2001/07/30 14:50:21 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -22,6 +22,7 @@ #include "libpq/libpq.h" #include "miscadmin.h" +#include "nodes/pg_list.h" #include "storage/fd.h" @@ -31,6 +32,13 @@ #define IDENT_USERNAME_MAX 512 /* Max size of username ident server can return */ +static List *hba_lines = NULL; /* A list of lists: entry for every line, + * list of tokens on each line. + */ + +static List *ident_lines = NULL;/* A list of lists: entry for every line, + * list of tokens on each line. + */ /* Some standard C libraries, including GNU, have an isblank() function. Others, including Solaris, do not. So we have our own. @@ -38,31 +46,31 @@ static bool isblank(const char c) { - return c == ' ' || c == 9 /* tab */ ; + return c == ' ' || c == 0x09;/* tab */ } +/* + * 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. + */ static void next_token(FILE *fp, char *buf, const int bufsz) { -/*-------------------------------------------------------------------------- - 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. ---------------------------------------------------------------------------*/ int c; char *eb = buf + (bufsz - 1); /* Move over inital token-delimiting blanks */ - while (isblank(c = getc(fp))); + while (isblank(c = getc(fp))) + ; if (c != '\n') { - /* * build a token in buf of next characters up to EOF, eol, or * blank. @@ -72,160 +80,214 @@ next_token(FILE *fp, char *buf, const int bufsz) if (buf < eb) *buf++ = c; c = getc(fp); - - /* - * Put back the char right after the token (putting back EOF - * is ok) - */ } + /* + * Put back the char right after the token (putting back EOF + * is ok) + */ ungetc(c, fp); } *buf = '\0'; } - static void -read_through_eol(FILE *file) +read_to_eol(FILE *file) { int c; - do - c = getc(file); - while (c != '\n' && c != EOF); + while ((c = getc(file)) != '\n' && c != EOF) + ; } - +/* + * Process the file line by line and create a list of list of tokens. + */ static void -read_hba_entry2(FILE *file, UserAuth *userauth_p, char *auth_arg, - bool *error_p) +tokenize_file(FILE *file, List **lines) { -/*-------------------------------------------------------------------------- - Read from file FILE the rest of a host record, after the mask field, - and return the interpretation of it as *userauth_p, auth_arg, and - *error_p. ----------------------------------------------------------------------------*/ char buf[MAX_TOKEN]; + List *next_line = NIL; + bool comment_found = false; - /* Get authentication type token. */ - next_token(file, buf, sizeof(buf)); - - if (strcmp(buf, "trust") == 0) - *userauth_p = uaTrust; - else if (strcmp(buf, "ident") == 0) - *userauth_p = uaIdent; - else if (strcmp(buf, "password") == 0) - *userauth_p = uaPassword; - else if (strcmp(buf, "krb4") == 0) - *userauth_p = uaKrb4; - else if (strcmp(buf, "krb5") == 0) - *userauth_p = uaKrb5; - else if (strcmp(buf, "reject") == 0) - *userauth_p = uaReject; - else if (strcmp(buf, "crypt") == 0) - *userauth_p = uaCrypt; - else + while (1) { - *error_p = true; + next_token(file, buf, sizeof(buf)); + if (feof(file)) + break; + /* trim off comment, even if inside a token */ + if (strstr(buf,"#") != NULL) + { + *strstr(buf,"#") = '\0'; + comment_found = true; + } + + /* add token to list */ if (buf[0] != '\0') - read_through_eol(file); + { + if (next_line == NIL) + { + /* make a new line List */ + next_line = lcons(pstrdup(buf), NIL); + *lines = lappend(*lines, next_line); + } + else + /* append token to line */ + next_line = lappend(next_line, pstrdup(buf)); + } + else + /* force a new List line */ + next_line = NIL; + + if (comment_found) + { + /* Skip the rest of the line */ + read_to_eol(file); + next_line = NIL; + comment_found = false; + } + } +} + + +/* + * Free memory used by lines/tokens + */ +static void free_lines(List **lines) +{ + if (*lines) + { + List *line, *token; + + foreach(line, *lines) + { + foreach(token,lfirst(line)) + pfree(lfirst(token)); + freeList(lfirst(line)); + } + freeList(*lines); + *lines = NULL; + } +} + + +/* + * Read from file FILE the rest of a host record, after the mask field, + * and return the interpretation of it as *userauth_p, auth_arg, and + * *error_p. + */ +static void +parse_hba_auth(List *line, UserAuth *userauth_p, char *auth_arg, + bool *error_p) +{ + char *token = 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, "crypt") == 0) + *userauth_p = uaCrypt; + else + *error_p = true; } if (!*error_p) { /* Get the authentication argument token, if any */ - next_token(file, buf, sizeof(buf)); - if (buf[0] == '\0') + line = lnext(line); + if (!line) auth_arg[0] = '\0'; else { - StrNCpy(auth_arg, buf, MAX_AUTH_ARG - 1); - next_token(file, buf, sizeof(buf)); - if (buf[0] != '\0') - { + StrNCpy(auth_arg, token, MAX_AUTH_ARG - 1); + /* If there is more on the line, it is an error */ + if (lnext(line)) *error_p = true; - read_through_eol(file); - } } } } - +/* + * Process the non-comment lines in the config file. + * + * See if it applies to a connection to a host with IP address "*raddr" + * to a database named "*database". If so, return *found_p true + * and *userauth_p and *auth_arg as the values from the entry. + * If not, leave *found_p as it was. If the record has a syntax error, + * return *error_p true, after issuing a message to stderr. If no error, + * leave *error_p as it was. + */ static void -process_hba_record(FILE *file, hbaPort *port, bool *matches_p, bool *error_p) +parse_hba(List *line, hbaPort *port, bool *found_p, bool *error_p) { -/*--------------------------------------------------------------------------- - Process the non-comment record in the config file that is next on the file. - See if it applies to a connection to a host with IP address "*raddr" - to a database named "*database". If so, return *matches_p true - and *userauth_p and *auth_arg as the values from the entry. - If not, leave *matches_p as it was. If the record has a syntax error, - return *error_p true, after issuing a message to stderr. If no error, - leave *error_p as it was. ----------------------------------------------------------------------------*/ - char db[MAX_TOKEN], - buf[MAX_TOKEN]; - - /* Read the record type field. */ - - next_token(file, buf, sizeof(buf)); - - if (buf[0] == '\0') - return; + char *db; + char *token; + Assert(line != NIL); + token = lfirst(line); /* Check the record type. */ - - if (strcmp(buf, "local") == 0) + if (strcmp(token, "local") == 0) { /* Get the database. */ - - next_token(file, db, sizeof(db)); - - if (db[0] == '\0') - goto syntax; - + line = lnext(line); + if (!line) + goto hba_syntax; + db = lfirst(line); + + line = lnext(line); + if (!line) + goto hba_syntax; /* Read the rest of the line. */ - - read_hba_entry2(file, &port->auth_method, port->auth_arg, error_p); + parse_hba_auth(line, &port->auth_method, port->auth_arg, error_p); + if (*error_p) + goto hba_syntax; /* * For now, disallow methods that need AF_INET sockets to work. */ - if (!*error_p && (port->auth_method == uaIdent || port->auth_method == uaKrb4 || port->auth_method == uaKrb5)) - *error_p = true; - - if (*error_p) - goto syntax; + goto hba_syntax; /* * If this record isn't for our database, or this is the wrong * sort of connection, ignore it. */ - if ((strcmp(db, port->database) != 0 && strcmp(db, "all") != 0 && (strcmp(db, "sameuser") != 0 || strcmp(port->database, port->user) != 0)) || port->raddr.sa.sa_family != AF_UNIX) return; } - else if (strcmp(buf, "host") == 0 || strcmp(buf, "hostssl") == 0) + else if (strcmp(token, "host") == 0 || strcmp(token, "hostssl") == 0) { - struct in_addr file_ip_addr, - mask; - bool discard = 0;/* Discard this entry */ + struct in_addr file_ip_addr, mask; #ifdef USE_SSL /* If SSL, then check that we are on SSL */ - if (strcmp(buf, "hostssl") == 0) + if (strcmp(token, "hostssl") == 0) { if (!port->ssl) - discard = 1; + return; /* Placeholder to require specific SSL level, perhaps? */ /* Or a client certificate */ @@ -234,67 +296,50 @@ process_hba_record(FILE *file, hbaPort *port, bool *matches_p, bool *error_p) } #else /* If not SSL, we don't support this */ - if (strcmp(buf, "hostssl") == 0) - goto syntax; + if (strcmp(token, "hostssl") == 0) + goto hba_syntax; #endif /* Get the database. */ - - next_token(file, db, sizeof(db)); - - if (db[0] == '\0') - goto syntax; + line = lnext(line); + if (!line) + goto hba_syntax; + db = lfirst(line); /* Read the IP address field. */ - - next_token(file, buf, sizeof(buf)); - - if (buf[0] == '\0') - goto syntax; + line = lnext(line); + if (!line) + goto hba_syntax; + token = lfirst(line); /* Remember the IP address field and go get mask field. */ - - if (!inet_aton(buf, &file_ip_addr)) - { - read_through_eol(file); - goto syntax; - } + 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); - next_token(file, buf, sizeof(buf)); - - if (buf[0] == '\0') - goto syntax; - - if (!inet_aton(buf, &mask)) - { - read_through_eol(file); - goto syntax; - } + if (!inet_aton(token, &mask)) + goto hba_syntax; /* * This is the record we're looking for. Read the rest of the * info from it. */ - - read_hba_entry2(file, &port->auth_method, port->auth_arg, error_p); - + line = lnext(line); + if (!line) + goto hba_syntax; + parse_hba_auth(line, &port->auth_method, port->auth_arg, error_p); if (*error_p) - goto syntax; - - /* - * If told to discard earlier. Moved down here so we don't get - * "out of sync" with the file. - */ - if (discard) - return; + goto hba_syntax; /* * If this record isn't for our database, or this is the wrong * sort of connection, ignore it. */ - if ((strcmp(db, port->database) != 0 && strcmp(db, "all") != 0 && (strcmp(db, "sameuser") != 0 || strcmp(port->database, port->user) != 0)) || port->raddr.sa.sa_family != AF_INET || @@ -302,98 +347,75 @@ process_hba_record(FILE *file, hbaPort *port, bool *matches_p, bool *error_p) return; } else - { - read_through_eol(file); - goto syntax; - } - - *matches_p = true; + goto hba_syntax; + /* Success */ + *found_p = true; return; -syntax: +hba_syntax: snprintf(PQerrormsg, PQERRORMSG_LENGTH, - "process_hba_record: invalid syntax in pg_hba.conf file\n"); + "parse_hba: invalid syntax in pg_hba.conf file\n"); fputs(PQerrormsg, stderr); pqdebug("%s", PQerrormsg); *error_p = true; + return; } - -static void -process_open_config_file(FILE *file, hbaPort *port, bool *hba_ok_p) +/* + * Process the hba file line by line. + */ +static bool +check_hba(hbaPort *port) { -/*--------------------------------------------------------------------------- - This function does the same thing as find_hba_entry, only with - the config file already open on stream descriptor "file". -----------------------------------------------------------------------------*/ - bool found_entry = false; /* found an applicable entry? */ - bool error = false; /* found an erroneous entry? */ - bool eof = false; /* end of hba file */ - - while (!eof && !found_entry && !error) - { - /* Process a line from the config file */ - int c = getc(file); + List *line; + bool found_entry = false; + bool error = false; - if (c == EOF) - eof = true; - else - { - ungetc(c, file); - if (c == '#') - read_through_eol(file); - else - process_hba_record(file, port, &found_entry, &error); - } + 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; - - *hba_ok_p = true; + return true; } + else + return false; } - -static void -find_hba_entry(hbaPort *port, bool *hba_ok_p) -{ /* - * Read the config file and find an entry that allows connection from - * host "raddr", user "user", to database "database". If found, - * return *hba_ok_p = true and *userauth_p and *auth_arg representing - * the contents of that entry. If there is no matching entry, we - * set *hba_ok_p = true, *userauth_p = uaReject. - * - * If the config file is unreadable or contains invalid syntax, we - * issue a diagnostic message to stderr (ie, the postmaster log file) - * and return without changing *hba_ok_p. - * + * 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. */ +static void +load_hba() +{ int fd, bufsize; FILE *file; /* The config file we have to read */ char *old_conf_file; - /* The name of old config file that better not exist. */ - - /* Fail if config file by old name exists. */ - - - /* put together the full pathname to the old config file */ + if (hba_lines) + free_lines(&hba_lines); + /* + * The name of old config file that better not exist. + * Fail if config file by old name exists. + * Put together the full pathname to the old config file. + */ bufsize = (strlen(DataDir) + strlen(OLD_CONF_FILE) + 2) * sizeof(char); old_conf_file = (char *) palloc(bufsize); snprintf(old_conf_file, bufsize, "%s/%s", DataDir, OLD_CONF_FILE); @@ -403,11 +425,10 @@ find_hba_entry(hbaPort *port, bool *hba_ok_p) /* Old config file exists. Tell this guy he needs to upgrade. */ close(fd); snprintf(PQerrormsg, PQERRORMSG_LENGTH, - "A file exists by the name used for host-based authentication " - "in prior releases of Postgres (%s). The name and format of " - "the configuration file have changed, so this file should be " - "converted.\n", - old_conf_file); + "A file exists by the name used for host-based authentication " + "in prior releases of Postgres (%s). The name and format of " + "the configuration file have changed, so this file should be " + "converted.\n", old_conf_file); fputs(PQerrormsg, stderr); pqdebug("%s", PQerrormsg); } @@ -425,16 +446,15 @@ find_hba_entry(hbaPort *port, bool *hba_ok_p) if (file == NULL) { /* The open of the config file failed. */ - snprintf(PQerrormsg, PQERRORMSG_LENGTH, - "find_hba_entry: Unable to open authentication config file \"%s\": %s\n", + "load_hba: Unable to open authentication config file \"%s\": %s\n", conf_file, strerror(errno)); fputs(PQerrormsg, stderr); pqdebug("%s", PQerrormsg); } else { - process_open_config_file(file, port, hba_ok_p); + tokenize_file(file, &hba_lines); FreeFile(file); } pfree(conf_file); @@ -443,35 +463,175 @@ find_hba_entry(hbaPort *port, bool *hba_ok_p) } +/* + * Take the line and compare it to the needed map, pg_user and ident_user. + */ static void -interpret_ident_response(char *ident_response, - bool *error_p, char *ident_username) +parse_ident_usermap(List *line, const char *usermap_name, const char *pg_user, + const char *ident_user, bool *found_p, bool *error_p) +{ + char *token; + char *file_map; + char *file_pguser; + char *file_ident_user; + + *error_p = false; + *found_p = false; + + /* A token read from the file */ + Assert(line != NIL); + token = lfirst(line); + file_map = token; + + line = lnext(line); + if (!line) + goto ident_syntax; + token = lfirst(line); + if (token[0] != '\0') + { + file_ident_user = token; + line = lnext(line); + if (!line) + goto ident_syntax; + token = lfirst(line); + if (token[0] != '\0') + { + file_pguser = token; + 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: + snprintf(PQerrormsg, PQERRORMSG_LENGTH, + "parse_ident_usermap: invalid syntax in pg_ident.conf file\n"); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + *error_p = true; + return; +} + + +/* + * Process the ident usermap file line by line. + */ +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') + { + snprintf(PQerrormsg, PQERRORMSG_LENGTH, + "load_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.\n"); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + 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; +} + + +/* + * See if the user with ident username "ident_user" is allowed to act + * as Postgres user "pguser" according to usermap "usermap_name". Look + * it up in the usermap file. + * + * 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 *checks_out_p == true. + */ +static void +load_ident() { -/*---------------------------------------------------------------------------- - Parse the string "*ident_response" as a response from a query to an Ident - server. If it's a normal response indicating a username, return - *error_p == false and the username as *ident_username. If it's anything - else, return *error_p == true and *ident_username undefined. -----------------------------------------------------------------------------*/ - char *cursor; /* Cursor into *ident_response */ + FILE *file; /* The map file we have to read */ + char *map_file; /* The name of the map file we have to + * read */ + int bufsize; - cursor = &ident_response[0]; + 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, PG_BINARY_R); + if (file == NULL) + { + /* The open of the map file failed. */ + snprintf(PQerrormsg, PQERRORMSG_LENGTH, + "load_ident_usermap: Unable to open usermap file \"%s\": %s\n", + map_file, strerror(errno)); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + } + else + { + tokenize_file(file, &ident_lines); + 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 username, return + * *error_p == false and the username as *ident_user. If it's anything + * else, return *error_p == true and *ident_user undefined. + */ +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) - *error_p = true; + return false; else if (ident_response[strlen(ident_response) - 2] != '\r') - *error_p = true; + return false; else { while (*cursor != ':' && *cursor != '\r') cursor++; /* skip port field */ if (*cursor != ':') - *error_p = true; + return false; else { /* We're positioned to colon before response type field */ @@ -482,24 +642,23 @@ interpret_ident_response(char *ident_response, while (isblank(*cursor)) cursor++; /* skip blanks */ i = 0; - while (*cursor != ':' && *cursor != '\r' && !isblank(*cursor) - && i < (int) (sizeof(response_type) - 1)) + 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) - *error_p = true; + return false; else { - /* * It's a USERID response. Good. "cursor" should be * pointing to the colon that precedes the operating * system type. */ if (*cursor != ':') - *error_p = true; + return false; else { cursor++; /* Go over colon */ @@ -507,10 +666,10 @@ interpret_ident_response(char *ident_response, while (*cursor != ':' && *cursor != '\r') cursor++; if (*cursor != ':') - *error_p = true; + return false; else { - int i; /* Index into *ident_username */ + int i; /* Index into *ident_user */ cursor++; /* Go over colon */ while (isblank(*cursor)) @@ -518,9 +677,9 @@ interpret_ident_response(char *ident_response, /* Rest of line is username. Copy it over. */ i = 0; while (*cursor != '\r' && i < IDENT_USERNAME_MAX) - ident_username[i++] = *cursor++; - ident_username[i] = '\0'; - *error_p = false; + ident_user[i++] = *cursor++; + ident_user[i] = '\0'; + return true; } } } @@ -529,39 +688,39 @@ interpret_ident_response(char *ident_response, } +/* + * 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 username the + * ident server gives as "*ident_user". -static void -ident(const struct in_addr remote_ip_addr, const struct in_addr local_ip_addr, - const ushort remote_port, const ushort local_port, - bool *ident_failed, char *ident_username) -{ -/*-------------------------------------------------------------------------- - 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 username the - ident server gives as "*ident_username". - - IP addresses and port numbers are in network byte order. - - But iff we're unable to get the information from ident, return - *ident_failed == true (and *ident_username undefined). -----------------------------------------------------------------------------*/ - + * IP addresses and port numbers are in network byte order. + * But iff we're unable to get the information from ident, return + * false. + */ +static int +ident(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) { snprintf(PQerrormsg, PQERRORMSG_LENGTH, - "Failed to create socket on which to talk to Ident server. " - "socket() returned errno = %s (%d)\n", - strerror(errno), errno); + "Failed to create socket on which to talk to Ident server. " + "socket() returned errno = %s (%d)\n", strerror(errno), errno); fputs(PQerrormsg, stderr); pqdebug("%s", PQerrormsg); + ident_return = false; } else { @@ -595,13 +754,13 @@ ident(const struct in_addr remote_ip_addr, const struct in_addr local_ip_addr, { snprintf(PQerrormsg, PQERRORMSG_LENGTH, "Unable to connect to Ident server on the host which is " - "trying to connect to Postgres " - "(IP address %s, Port %d). " - "errno = %s (%d)\n", - inet_ntoa(remote_ip_addr), IDENT_PORT, strerror(errno), errno); + "trying to connect to Postgres " + "(IP address %s, Port %d). " + "errno = %s (%d)\n", + inet_ntoa(remote_ip_addr), IDENT_PORT, strerror(errno), errno); fputs(PQerrormsg, stderr); pqdebug("%s", PQerrormsg); - *ident_failed = true; + ident_return = false; } else { @@ -614,260 +773,106 @@ ident(const struct in_addr remote_ip_addr, const struct in_addr local_ip_addr, if (rc < 0) { snprintf(PQerrormsg, PQERRORMSG_LENGTH, - "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. " - "errno = %s (%d)\n", - inet_ntoa(remote_ip_addr), IDENT_PORT, strerror(errno), errno); + "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. " + "errno = %s (%d)\n", + inet_ntoa(remote_ip_addr), IDENT_PORT, strerror(errno), errno); fputs(PQerrormsg, stderr); pqdebug("%s", PQerrormsg); - *ident_failed = true; + ident_return = false; } else { char ident_response[80 + IDENT_USERNAME_MAX]; - rc = recv(sock_fd, ident_response, sizeof(ident_response) - 1, 0); + rc = recv(sock_fd, ident_response, + sizeof(ident_response) - 1, 0); if (rc < 0) { snprintf(PQerrormsg, PQERRORMSG_LENGTH, - "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. " - "errno = %s (%d)\n", - inet_ntoa(remote_ip_addr), IDENT_PORT, - strerror(errno), errno); + "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. " + "errno = %s (%d)\n", + inet_ntoa(remote_ip_addr), IDENT_PORT, + strerror(errno), errno); fputs(PQerrormsg, stderr); pqdebug("%s", PQerrormsg); - *ident_failed = true; + ident_return = false; } else { - bool error; /* response from Ident is garbage. */ - ident_response[rc] = '\0'; - interpret_ident_response(ident_response, &error, ident_username); - *ident_failed = error; + ident_return = interpret_ident_response(ident_response, + ident_user); } } close(sock_fd); } } + return ident_return; } - -static void -parse_map_record(FILE *file, - char *file_map, char *file_pguser, char *file_iuser) -{ -/*--------------------------------------------------------------------------- - Take the noncomment line which is next on file "file" and interpret - it as a line in a usermap file. Specifically, return the first - 3 tokens as file_map, file_iuser, and file_pguser, respectively. If - there are fewer than 3 tokens, return null strings for the missing - ones. - ----------------------------------------------------------------------------*/ - char buf[MAX_TOKEN]; - - /* A token read from the file */ - - /* Set defaults in case fields not in file */ - file_map[0] = '\0'; - file_pguser[0] = '\0'; - file_iuser[0] = '\0'; - - next_token(file, buf, sizeof(buf)); - if (buf[0] != '\0') - { - strcpy(file_map, buf); - next_token(file, buf, sizeof(buf)); - if (buf[0] != '\0') - { - strcpy(file_iuser, buf); - next_token(file, buf, sizeof(buf)); - if (buf[0] != '\0') - { - strcpy(file_pguser, buf); - read_through_eol(file); - return; - } - } - snprintf(PQerrormsg, PQERRORMSG_LENGTH, - "Incomplete line in pg_ident: %s", file_map); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); - } -} - - - -static void -verify_against_open_usermap(FILE *file, - const char *pguser, - const char *ident_username, - const char *usermap_name, - bool *checks_out_p) -{ -/*-------------------------------------------------------------------------- - This function does the same thing as verify_against_usermap, - only with the config file already open on stream descriptor "file". ----------------------------------------------------------------------------*/ - bool match; /* We found a matching entry in the map - * file */ - bool eof; /* We've reached the end of the file we're - * reading */ - - match = false; /* initial value */ - eof = false; /* initial value */ - while (!eof && !match) - { - /* Process a line from the map file */ - - int c; /* a character read from the file */ - - c = getc(file); - ungetc(c, file); - if (c == EOF) - eof = true; - else - { - if (c == '#') - read_through_eol(file); - else - { - /* The following are fields read from a record of the file */ - char file_map[MAX_TOKEN + 1]; - char file_pguser[MAX_TOKEN + 1]; - char file_iuser[MAX_TOKEN + 1]; - - parse_map_record(file, file_map, file_pguser, file_iuser); - if (strcmp(file_map, usermap_name) == 0 && - strcmp(file_pguser, pguser) == 0 && - strcmp(file_iuser, ident_username) == 0) - match = true; - } - } - } - *checks_out_p = match; -} - - - -static void -verify_against_usermap(const char *pguser, - const char *ident_username, - const char *usermap_name, - bool *checks_out_p) +/* + * Talk to the ident server on the remote host and find out who owns the + * connection described by "port". Then look in the usermap file under + * the usermap *auth_arg and see if that user is equivalent to + * Postgres user *user. + * + * Return STATUS_OK if yes. + */ +int +authident(struct sockaddr_in *raddr, struct sockaddr_in *laddr, + const char *pg_user, const char *auth_arg) { -/*-------------------------------------------------------------------------- - See if the user with ident username "ident_username" is allowed to act - as Postgres user "pguser" according to usermap "usermap_name". Look - it up in the usermap file. - - Special case: For usermap "sameuser", don't look in the usermap - file. That's an implied map where "pguser" must be identical to - "ident_username" in order to be authorized. - - Iff authorized, return *checks_out_p == true. + /* We were unable to get ident to give us a username */ + char ident_user[IDENT_USERNAME_MAX + 1]; ---------------------------------------------------------------------------*/ + /* The username returned by ident */ + if (!ident(raddr->sin_addr, laddr->sin_addr, + raddr->sin_port, laddr->sin_port, ident_user)) + return STATUS_ERROR; - if (usermap_name[0] == '\0') - { - *checks_out_p = false; - snprintf(PQerrormsg, PQERRORMSG_LENGTH, - "verify_against_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.\n"); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); - } - else if (strcmp(usermap_name, "sameuser") == 0) - { - if (strcmp(ident_username, pguser) == 0) - *checks_out_p = true; - else - *checks_out_p = false; - } + if (check_ident_usermap(auth_arg, pg_user, ident_user)) + return STATUS_OK; else - { - FILE *file; /* The map file we have to read */ - char *map_file; /* The name of the map file we have to - * read */ - int bufsize; - - /* 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, PG_BINARY_R); - if (file == NULL) - { - /* The open of the map file failed. */ - - snprintf(PQerrormsg, PQERRORMSG_LENGTH, - "verify_against_usermap: Unable to open usermap file \"%s\": %s\n", - map_file, strerror(errno)); - fputs(PQerrormsg, stderr); - pqdebug("%s", PQerrormsg); - - *checks_out_p = false; - } - else - { - verify_against_open_usermap(file, - pguser, ident_username, usermap_name, - checks_out_p); - FreeFile(file); - } - pfree(map_file); - - - } + return STATUS_ERROR; } - +/* + * Determine what authentication method should be used when accessing database + * "database" from frontend "raddr", user "user". Return the method, + * an optional argument, 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 -authident(struct sockaddr_in * raddr, struct sockaddr_in * laddr, - const char *postgres_username, - const char *auth_arg) +hba_getauthmethod(hbaPort *port) { -/*--------------------------------------------------------------------------- - Talk to the ident server on the remote host and find out who owns the - connection described by "port". Then look in the usermap file under - the usermap *auth_arg and see if that user is equivalent to - Postgres user *user. - - Return STATUS_OK if yes. ----------------------------------------------------------------------------*/ - bool checks_out; - bool ident_failed; - /* We were unable to get ident to give us a username */ - char ident_username[IDENT_USERNAME_MAX + 1]; - - /* The username returned by ident */ - - ident(raddr->sin_addr, laddr->sin_addr, - raddr->sin_port, laddr->sin_port, - &ident_failed, ident_username); - - if (ident_failed) + if (check_hba(port)) + return STATUS_OK; + else return STATUS_ERROR; +} - verify_against_usermap(postgres_username, ident_username, auth_arg, - &checks_out); - - return checks_out ? STATUS_OK : STATUS_ERROR; +/* + * Clear tokenized file contents and force reload on next use. + */ +void load_hba_and_ident(void) +{ + load_hba(); + load_ident(); } +/* Character set stuff. Not sure it really belongs in this file. */ + #ifdef CYR_RECODE #define CHARSET_FILE "charset.conf" #define MAX_CHARSETS 10 @@ -882,8 +887,9 @@ struct CharsetItem char Table[MAX_TOKEN]; }; + static bool -InRange(char *buf, int host) +CharSetInRange(char *buf, int host) { int valid, i, @@ -989,7 +995,7 @@ GetCharSetByHost(char *TableName, int host, const char *DataDir) else { if (c == '#') - read_through_eol(file); + read_to_eol(file); else { /* Read the key */ @@ -1009,7 +1015,7 @@ GetCharSetByHost(char *TableName, int host, const char *DataDir) next_token(file, buf, sizeof(buf)); if (buf[0] != '\0') { - if (InRange(buf, host)) + if (CharSetInRange(buf, host)) { /* Read the charset */ next_token(file, buf, sizeof(buf)); @@ -1050,7 +1056,7 @@ GetCharSetByHost(char *TableName, int host, const char *DataDir) } break; } - read_through_eol(file); + read_to_eol(file); } } } @@ -1066,23 +1072,7 @@ GetCharSetByHost(char *TableName, int host, const char *DataDir) pfree((struct CharsetItem *) ChArray[i]); } } - #endif -int -hba_getauthmethod(hbaPort *port) -{ -/*--------------------------------------------------------------------------- - Determine what authentication method should be used when accessing database - "database" from frontend "raddr", user "user". Return the method, - an optional argument, 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. -----------------------------------------------------------------------------*/ - bool hba_ok = false; - - find_hba_entry(port, &hba_ok); - - return hba_ok ? STATUS_OK : STATUS_ERROR; -} + + diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index 3e92f16720..c1cd24c7d6 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -37,7 +37,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.231 2001/07/03 16:52:12 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.232 2001/07/30 14:50:22 momjian Exp $ * * NOTES * @@ -809,6 +809,8 @@ ServerLoop(void) nSockets = initMasks(&readmask, &writemask); + load_hba_and_ident(); + for (;;) { Port *port; @@ -874,6 +876,7 @@ ServerLoop(void) if (got_SIGHUP) { got_SIGHUP = false; + load_hba_and_ident(); ProcessConfigFile(PGC_SIGHUP); } @@ -993,7 +996,7 @@ ProcessStartupPacket(Port *port, bool SSLdone) buf = palloc(len); pq_getbytes(buf, len); - + packet = buf; /* @@ -1479,7 +1482,7 @@ reaper(SIGNAL_ARGS) #endif /* * Check if this child was the statistics collector. If - * so, start a new one. + * so, start a new one. */ if (pgstat_ispgstat(pid)) { @@ -1987,19 +1990,8 @@ DoBackend(Port *port) av[ac++] = "-o"; av[ac++] = ttybuf; } - av[ac] = (char *) NULL; - /* - * Release postmaster's working memory context so that backend can - * recycle the space. Note this does not trash *MyProcPort, because - * ConnCreate() allocated that space with malloc() ... else we'd need - * to copy the Port data here. - */ - MemoryContextSwitchTo(TopMemoryContext); - MemoryContextDelete(PostmasterContext); - PostmasterContext = NULL; - /* * Debug: print arguments being passed to backend */ diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index acf89356e8..0de677a944 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.227 2001/06/29 16:05:56 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.228 2001/07/30 14:50:24 momjian Exp $ * * NOTES * this is the "main" module of the postgres backend and @@ -1120,7 +1120,7 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[], const cha unsigned short remote_port; char *potential_DataDir = NULL; - + /* * Catch standard options before doing much else. This even works on * systems without getopt_long. @@ -1144,16 +1144,27 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[], const cha * * If we are running under the postmaster, this is done already. */ - if (!IsUnderPostmaster) + if (IsUnderPostmaster) + { + MemoryContextSwitchTo(TopMemoryContext); + ClientAuthentication(MyProcPort); /* might not return */ + /* + * Release postmaster's working memory context so that backend can + * recycle the space. Note this does not trash *MyProcPort, because + * ConnCreate() allocated that space with malloc() ... else we'd need + * to copy the Port data here. We delete it here because the + * authorization file tokens are stored in this context. + */ + MemoryContextDelete(PostmasterContext); + PostmasterContext = NULL; + } + else { SetProcessingMode(InitProcessing); EnableExceptionHandling(true); MemoryContextInit(); } - if (IsUnderPostmaster) - ClientAuthentication(MyProcPort); /* might not return */ - /* * Set default values for command-line options. */ @@ -1714,7 +1725,7 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[], const cha if (!IsUnderPostmaster) { puts("\nPOSTGRES backend interactive interface "); - puts("$Revision: 1.227 $ $Date: 2001/06/29 16:05:56 $\n"); + puts("$Revision: 1.228 $ $Date: 2001/07/30 14:50:24 $\n"); } /* diff --git a/src/include/libpq/hba.h b/src/include/libpq/hba.h index 9afd9f8417..f6493373e2 100644 --- a/src/include/libpq/hba.h +++ b/src/include/libpq/hba.h @@ -4,7 +4,7 @@ * Interface to hba.c * * - * $Id: hba.h,v 1.19 2001/03/22 04:00:47 momjian Exp $ + * $Id: hba.h,v 1.20 2001/07/30 14:50:24 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -40,8 +40,9 @@ typedef enum UserAuth typedef struct Port hbaPort; -int hba_getauthmethod(hbaPort *port); +int hba_getauthmethod(hbaPort *port); int authident(struct sockaddr_in * raddr, struct sockaddr_in * laddr, const char *postgres_username, const char *auth_arg); +void load_hba_and_ident(void); #endif -- 2.40.0