📄 hba.c
字号:
(errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("missing entry in file \"%s\" at end of line %d", IdentFileName, line_number))); *error_p = true;}/* * 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 "pgrole" according to usermap "usermap_name". * * Special case: For usermap "samerole", don't look in the usermap * file. That's an implied map where "pgrole" must be identical to * "ident_user" in order to be authorized. * * Iff authorized, return true. */static boolcheck_ident_usermap(const char *usermap_name, const char *pg_role, const char *ident_user){ bool found_entry = false, error = false; if (usermap_name == NULL || usermap_name[0] == '\0') { ereport(LOG, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("cannot use Ident authentication without usermap field"))); found_entry = false; } else if (strcmp(usermap_name, "sameuser\n") == 0 || strcmp(usermap_name, "samerole\n") == 0) { if (strcmp(pg_role, ident_user) == 0) found_entry = true; else found_entry = false; } else { ListCell *line_cell, *num_cell; forboth(line_cell, ident_lines, num_cell, ident_line_nums) { parse_ident_usermap(lfirst(line_cell), lfirst_int(num_cell), usermap_name, pg_role, 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. */voidload_ident(void){ FILE *file; if (ident_lines || ident_line_nums) free_lines(&ident_lines, &ident_line_nums); file = AllocateFile(IdentFileName, "r"); if (file == NULL) { /* not fatal ... we just won't do any special ident maps */ ereport(LOG, (errcode_for_file_access(), errmsg("could not open Ident usermap file \"%s\": %m", IdentFileName))); } else { tokenize_file(IdentFileName, file, &ident_lines, &ident_line_nums); FreeFile(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 boolinterpret_ident_response(const char *ident_response, char *ident_user){ const 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 (pg_isblank(*cursor)) cursor++; /* skip blanks */ i = 0; while (*cursor != ':' && *cursor != '\r' && !pg_isblank(*cursor) && i < (int) (sizeof(response_type) - 1)) response_type[i++] = *cursor++; response_type[i] = '\0'; while (pg_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 (pg_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 boolident_inet(const SockAddr remote_addr, const SockAddr local_addr, 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; char remote_addr_s[NI_MAXHOST]; char remote_port[NI_MAXSERV]; char local_addr_s[NI_MAXHOST]; char local_port[NI_MAXSERV]; char ident_port[NI_MAXSERV]; char ident_query[80]; char ident_response[80 + IDENT_USERNAME_MAX]; struct addrinfo *ident_serv = NULL, *la = NULL, hints; /* * Might look a little weird to first convert it to text and then back to * sockaddr, but it's protocol independent. */ pg_getnameinfo_all(&remote_addr.addr, remote_addr.salen, remote_addr_s, sizeof(remote_addr_s), remote_port, sizeof(remote_port), NI_NUMERICHOST | NI_NUMERICSERV); pg_getnameinfo_all(&local_addr.addr, local_addr.salen, local_addr_s, sizeof(local_addr_s), local_port, sizeof(local_port), NI_NUMERICHOST | NI_NUMERICSERV); snprintf(ident_port, sizeof(ident_port), "%d", IDENT_PORT); hints.ai_flags = AI_NUMERICHOST; hints.ai_family = remote_addr.addr.ss_family; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = 0; hints.ai_addrlen = 0; hints.ai_canonname = NULL; hints.ai_addr = NULL; hints.ai_next = NULL; rc = pg_getaddrinfo_all(remote_addr_s, ident_port, &hints, &ident_serv); if (rc || !ident_serv) { if (ident_serv) pg_freeaddrinfo_all(hints.ai_family, ident_serv); return false; /* we don't expect this to happen */ } hints.ai_flags = AI_NUMERICHOST; hints.ai_family = local_addr.addr.ss_family; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = 0; hints.ai_addrlen = 0; hints.ai_canonname = NULL; hints.ai_addr = NULL; hints.ai_next = NULL; rc = pg_getaddrinfo_all(local_addr_s, NULL, &hints, &la); if (rc || !la) { if (la) pg_freeaddrinfo_all(hints.ai_family, la); return false; /* we don't expect this to happen */ } sock_fd = socket(ident_serv->ai_family, ident_serv->ai_socktype, ident_serv->ai_protocol); if (sock_fd < 0) { ereport(LOG, (errcode_for_socket_access(), errmsg("could not create socket for Ident connection: %m"))); ident_return = false; goto ident_inet_done; } /* * 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. */ rc = bind(sock_fd, la->ai_addr, la->ai_addrlen); if (rc != 0) { ereport(LOG, (errcode_for_socket_access(), errmsg("could not bind to local address \"%s\": %m", local_addr_s))); ident_return = false; goto ident_inet_done; } rc = connect(sock_fd, ident_serv->ai_addr, ident_serv->ai_addrlen); if (rc != 0) { ereport(LOG, (errcode_for_socket_access(), errmsg("could not connect to Ident server at address \"%s\", port %s: %m", remote_addr_s, ident_port))); ident_return = false; goto ident_inet_done; } /* The query we send to the Ident server */ snprintf(ident_query, sizeof(ident_query), "%s,%s\r\n", remote_port, 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) { ereport(LOG, (errcode_for_socket_access(), errmsg("could not send query to Ident server at address \"%s\", port %s: %m", remote_addr_s, ident_port))); ident_return = false; goto ident_inet_done; } do { rc = recv(sock_fd, ident_response, sizeof(ident_response) - 1, 0); } while (rc < 0 && errno == EINTR); if (rc < 0) { ereport(LOG, (errcode_for_socket_access(), errmsg("could not receive response from Ident server at address \"%s\", port %s: %m", remote_addr_s, ident_port))); ident_return = false; goto ident_inet_done; } ident_response[rc] = '\0'; ident_return = interpret_ident_response(ident_response, ident_user); if (!ident_return) ereport(LOG, (errmsg("invalidly formatted response from Ident server: \"%s\"", ident_response)));ident_inet_done: if (sock_fd >= 0) closesocket(sock_fd); pg_freeaddrinfo_all(remote_addr.addr.ss_family, ident_serv); pg_freeaddrinfo_all(local_addr.addr.ss_family, la); 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. */#ifdef HAVE_UNIX_SOCKETSstatic boolident_unix(int sock, char *ident_user){#if defined(HAVE_GETPEEREID) /* OpenBSD style: */ uid_t uid; gid_t gid; struct passwd *pass; errno = 0; if (getpeereid(sock, &uid, &gid) != 0) { /* We didn't get a valid credentials struct. */ ereport(LOG, (errcode_for_socket_access(), errmsg("could not get peer credentials: %m"))); return false; } pass = getpwuid(uid); if (pass == NULL) { ereport(LOG, (errmsg("local user with ID %d does not exist", (int) uid))); return false; } StrNCpy(ident_user, pass->pw_name, IDENT_USERNAME_MAX + 1); return true;#elif 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. */ ereport(LOG, (errcode_for_socket_access(), errmsg("could not get peer credentials: %m"))); return false; } pass = getpwuid(peercred.uid); if (pass == NULL) { ereport(LOG, (errmsg("local user with ID %d does not exist", (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 */#if defined(HAVE_STRUCT_CMSGCRED) typedef struct cmsgcred Cred;#define cruid cmcred_uid#elif defined(HAVE_STRUCT_FCRED) typedef struct fcred Cred;#define cruid fc_uid#elif defined(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) { ereport(LOG, (errcode_for_socket_access(), errmsg("could not get peer credentials: %m"))); return false; } cred = (Cred *) CMSG_DATA(cmsg); pw = getpwuid(cred->cruid); if (pw == NULL) { ereport(LOG, (errmsg("local user with ID %d does not exist", (int) cred->cruid))); return false; } StrNCpy(ident_user, pw->pw_name, IDENT_USERNAME_MAX + 1); return true;#else ereport(LOG, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("Ident authentication is not supported on local connections on this platform"))); return false;#endif}#endif /* HAVE_UNIX_SOCKETS *//* * 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). */intauthident(hbaPort *port){ char ident_user[IDENT_USERNAME_MAX + 1]; switch (port->raddr.addr.ss_family) { case AF_INET:#ifdef HAVE_IPV6 case AF_INET6:#endif if (!ident_inet(port->raddr, port->laddr, ident_user)) return STATUS_ERROR; break;#ifdef HAVE_UNIX_SOCKETS case AF_UNIX: if (!ident_unix(port->sock, ident_user)) return STATUS_ERROR; break;#endif default: return STATUS_ERROR; } ereport(DEBUG2, (errmsg("Ident protocol identifies remote user as \"%s\"", ident_user))); if (check_ident_usermap(port->auth_arg, port->user_name, 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. */inthba_getauthmethod(hbaPort *port){ if (check_hba(port)) return STATUS_OK; else return STATUS_ERROR;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -