📄 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). * * $Id: hba.c,v 1.43 1999/05/25 16:08:59 momjian Exp $ * *------------------------------------------------------------------------- */#include <stdio.h>#include <string.h>#include <errno.h>#include <pwd.h>#include <sys/types.h>#include <fcntl.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include <unistd.h>#include <postgres.h>#include <miscadmin.h>#include <libpq/libpq.h>#include <libpq/pqcomm.h>#include <libpq/hba.h>#include <port/inet_aton.h> /* For inet_aton() */#include <storage/fd.h>/* Some standard C libraries, including GNU, have an isblank() function. Others, including Solaris, do not. So we have our own.*/static boolisblank(const char c){ return c == ' ' || c == 9 /* tab */ ;}static voidnext_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))); if (c != '\n') { /* * build a token in buf of next characters up to EOF, eol, or * blank. */ while (c != EOF && c != '\n' && !isblank(c)) { if (buf < eb) *buf++ = c; c = getc(fp); /* * Put back the char right after the token (putting back EOF * is ok) */ } ungetc(c, fp); } *buf = '\0';}static voidread_through_eol(FILE *file){ int c; do c = getc(file); while (c != '\n' && c != EOF);}static voidread_hba_entry2(FILE *file, UserAuth *userauth_p, char *auth_arg, bool *error_p){/*-------------------------------------------------------------------------- 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]; /* 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 { *error_p = true; if (buf[0] != '\0') read_through_eol(file); } if (!*error_p) { /* Get the authentication argument token, if any */ next_token(file, buf, sizeof(buf)); if (buf[0] == '\0') auth_arg[0] = '\0'; else { StrNCpy(auth_arg, buf, MAX_AUTH_ARG - 1); next_token(file, buf, sizeof(buf)); if (buf[0] != '\0') { *error_p = true; read_through_eol(file); } } }}static voidprocess_hba_record(FILE *file, SockAddr *raddr, const char *user, const char *database, bool *matches_p, bool *error_p, UserAuth *userauth_p, char *auth_arg){/*--------------------------------------------------------------------------- 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; /* Check the record type. */ if (strcmp(buf, "local") == 0) { /* Get the database. */ next_token(file, db, sizeof(db)); if (db[0] == '\0') goto syntax; /* Read the rest of the line. */ read_hba_entry2(file, userauth_p, auth_arg, error_p); /* * For now, disallow methods that need AF_INET sockets to work. */ if (!*error_p && (*userauth_p == uaIdent || *userauth_p == uaKrb4 || *userauth_p == uaKrb5)) *error_p = true; if (*error_p) goto syntax; /* * If this record isn't for our database, or this is the wrong * sort of connection, ignore it. */ if ((strcmp(db, database) != 0 && strcmp(db, "all") != 0 && (strcmp(db, "sameuser") != 0 || strcmp(database, user) != 0)) || raddr->sa.sa_family != AF_UNIX) return; } else if (strcmp(buf, "host") == 0) { struct in_addr file_ip_addr, mask; /* Get the database. */ next_token(file, db, sizeof(db)); if (db[0] == '\0') goto syntax; /* Read the IP address field. */ next_token(file, buf, sizeof(buf)); if (buf[0] == '\0') goto syntax; /* Remember the IP address field and go get mask field. */ if (!inet_aton(buf, &file_ip_addr)) { read_through_eol(file); goto syntax; } /* Read the mask field. */ next_token(file, buf, sizeof(buf)); if (buf[0] == '\0') goto syntax; if (!inet_aton(buf, &mask)) { read_through_eol(file); goto syntax; } /* * This is the record we're looking for. Read the rest of the * info from it. */ read_hba_entry2(file, userauth_p, auth_arg, error_p); if (*error_p) goto syntax; /* * If this record isn't for our database, or this is the wrong * sort of connection, ignore it. */ if ((strcmp(db, database) != 0 && strcmp(db, "all") != 0 && (strcmp(db, "sameuser") != 0 || strcmp(database, user) != 0)) || raddr->sa.sa_family != AF_INET || ((file_ip_addr.s_addr ^ raddr->in.sin_addr.s_addr) & mask.s_addr) != 0x0000) return; } else { read_through_eol(file); goto syntax; } *matches_p = true; return;syntax: snprintf(PQerrormsg, ERROR_MSG_LENGTH, "process_hba_record: invalid syntax in pg_hba.conf file\n"); fputs(PQerrormsg, stderr); pqdebug("%s", PQerrormsg); *error_p = true;}static voidprocess_open_config_file(FILE *file, SockAddr *raddr, const char *user, const char *database, bool *hba_ok_p, UserAuth *userauth_p, char *auth_arg){/*--------------------------------------------------------------------------- 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); if (c == EOF) eof = true; else { ungetc(c, file); if (c == '#') read_through_eol(file); else process_hba_record(file, raddr, user, database, &found_entry, &error, userauth_p, auth_arg); } } if (!error) { /* If no matching entry was found, synthesize 'reject' entry. */ if (!found_entry) *userauth_p = uaReject; *hba_ok_p = true; }}static voidfind_hba_entry(SockAddr *raddr, const char *user, const char *database, bool *hba_ok_p, UserAuth *userauth_p, char *auth_arg){/* * 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. * * 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. */ 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 */ 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);#ifndef __CYGWIN32__ if ((fd = open(old_conf_file, O_RDONLY, 0)) != -1)#else if ((fd = open(old_conf_file, O_RDONLY | O_BINARY, 0)) != -1)#endif { /* Old config file exists. Tell this guy he needs to upgrade. */ close(fd); snprintf(PQerrormsg, ERROR_MSG_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); fputs(PQerrormsg, stderr); pqdebug("%s", PQerrormsg); } else { char *conf_file; /* The name of the config file we have to * read */ /* 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. */ snprintf(PQerrormsg, ERROR_MSG_LENGTH, "find_hba_entry: Host-based authentication config file " "does not exist or permissions are not setup correctly! " "Unable to open file \"%s\".\n", conf_file); fputs(PQerrormsg, stderr); pqdebug("%s", PQerrormsg); } else { process_open_config_file(file, raddr, user, database, hba_ok_p, userauth_p, auth_arg); FreeFile(file); } pfree(conf_file); } pfree(old_conf_file);}static voidinterpret_ident_response(char *ident_response, bool *error_p, char *ident_username){/*---------------------------------------------------------------------------- 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 */ cursor = &ident_response[0]; /* * Ident's response, in the telnet tradition, should end in crlf * (\r\n). */ if (strlen(ident_response) < 2) *error_p = true; else if (ident_response[strlen(ident_response) - 2] != '\r') *error_p = true; else { while (*cursor != ':' && *cursor != '\r') cursor++; /* skip port field */ if (*cursor != ':') *error_p = true; 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 < 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; 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; else { cursor++; /* Go over colon */ /* Skip over operating system field. */ while (*cursor != ':' && *cursor != '\r') cursor++; if (*cursor != ':') *error_p = true; else { int i; /* Index into *ident_username */ cursor++; /* Go over colon */ while (isblank(*cursor)) cursor++; /* skip blanks */ /* 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; } } } } }}static voidident(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).----------------------------------------------------------------------------*/ int sock_fd, /* File descriptor for socket on which we * talk to Ident */ rc; /* Return code from a locally called * function */ sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_IP); if (sock_fd == -1)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -