hba.c

来自「PostgreSQL7.4.6 for Linux」· C语言 代码 · 共 1,584 行 · 第 1/3 页

C
1,584
字号
 * *	Iff authorized, return true. */static boolcheck_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 == 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") == 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. */voidload_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)	{		ereport(LOG,				(errcode_for_file_access(),				 errmsg("could not open Ident 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 boolinterpret_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 (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.	 */	getnameinfo_all(&remote_addr.addr, remote_addr.salen,					remote_addr_s, sizeof(remote_addr_s),					remote_port, sizeof(remote_port),					NI_NUMERICHOST | NI_NUMERICSERV);	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 = getaddrinfo_all(remote_addr_s, ident_port, &hints, &ident_serv);	if (rc || !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 = getaddrinfo_all(local_addr_s, NULL, &hints, &la);	if (rc || !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);ident_inet_done:	if (sock_fd >= 0)		closesocket(sock_fd);	freeaddrinfo_all(remote_addr.addr.ss_family, ident_serv);	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;	}	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 + =
减小字号Ctrl + -
显示快捷键?