1 /*-------------------------------------------------------------------------
4 * Routines to handle host based authentication (that's the scheme
5 * wherein you authenticate a user by seeing what IP address the system
6 * says he comes from and possibly using ident).
10 * $Header: /cvsroot/pgsql/src/backend/libpq/hba.c,v 1.26 1998/01/26 01:41:08 scrappy Exp $
12 *-------------------------------------------------------------------------
18 #include <sys/types.h>
20 #include <sys/socket.h>
21 #include <netinet/in.h>
22 #include <arpa/inet.h>
26 #include <miscadmin.h>
27 #include <libpq/libpq.h>
28 #include <libpq/pqcomm.h>
29 #include <libpq/hba.h>
30 #include <port/inet_aton.h> /* For inet_aton() */
31 #include <storage/fd.h>
33 /* Some standard C libraries, including GNU, have an isblank() function.
34 Others, including Solaris, do not. So we have our own.
39 return (c == ' ' || c == 9 /* tab */ );
45 next_token(FILE *fp, char *buf, const int bufsz)
47 /*--------------------------------------------------------------------------
48 Grab one token out of fp. Tokens are strings of non-blank
49 characters bounded by blank characters, beginning of line, and end
50 of line. Blank means space or tab. Return the token as *buf.
51 Leave file positioned to character immediately after the token or
52 EOF, whichever comes first. If no more tokens on line, return null
53 string as *buf and position file to beginning of next line or EOF,
54 whichever comes first.
55 --------------------------------------------------------------------------*/
57 char *eb = buf + (bufsz - 1);
59 /* Move over inital token-delimiting blanks */
60 while (isblank(c = getc(fp)));
66 * build a token in buf of next characters up to EOF, eol, or
69 while (c != EOF && c != '\n' && !isblank(c))
76 * Put back the char right after the token (putting back EOF
88 read_through_eol(FILE *file)
94 while (c != '\n' && c != EOF);
100 read_hba_entry2(FILE *file, UserAuth * userauth_p, char auth_arg[],
103 /*--------------------------------------------------------------------------
104 Read from file FILE the rest of a host record, after the mask field,
105 and return the interpretation of it as *userauth_p, auth_arg, and
107 ---------------------------------------------------------------------------*/
110 /* Get authentication type token. */
111 next_token(file, buf, sizeof(buf));
113 if (strcmp(buf, "trust") == 0)
114 *userauth_p = uaTrust;
115 else if (strcmp(buf, "ident") == 0)
116 *userauth_p = uaIdent;
117 else if (strcmp(buf, "password") == 0)
118 *userauth_p = uaPassword;
119 else if (strcmp(buf, "krb4") == 0)
120 *userauth_p = uaKrb4;
121 else if (strcmp(buf, "krb5") == 0)
122 *userauth_p = uaKrb5;
123 else if (strcmp(buf, "reject") == 0)
124 *userauth_p = uaReject;
125 else if (strcmp(buf, "crypt") == 0)
126 *userauth_p = uaCrypt;
132 read_through_eol(file);
137 /* Get the authentication argument token, if any */
138 next_token(file, buf, sizeof(buf));
143 StrNCpy(auth_arg, buf, MAX_AUTH_ARG - 1);
144 next_token(file, buf, sizeof(buf));
148 read_through_eol(file);
157 process_hba_record(FILE *file, SockAddr *raddr, const char database[],
158 bool *matches_p, bool *error_p,
159 UserAuth * userauth_p, char auth_arg[])
161 /*---------------------------------------------------------------------------
162 Process the non-comment record in the config file that is next on the file.
163 See if it applies to a connection to a host with IP address "*raddr"
164 to a database named "database[]". If so, return *matches_p true
165 and *userauth_p and auth_arg[] as the values from the entry.
166 If not, leave *matches_p as it was. If the record has a syntax error,
167 return *error_p true, after issuing a message to stderr. If no error,
168 leave *error_p as it was.
169 ---------------------------------------------------------------------------*/
170 char db[MAX_TOKEN], buf[MAX_TOKEN];
172 /* Read the record type field. */
174 next_token(file, buf, sizeof(buf));
179 /* Check the record type. */
181 if (strcmp(buf, "local") == 0)
183 /* Get the database. */
185 next_token(file, db, sizeof(db));
190 /* Read the rest of the line. */
192 read_hba_entry2(file, userauth_p, auth_arg, error_p);
195 * For now, disallow methods that need AF_INET sockets to work.
199 (*userauth_p == uaIdent ||
200 *userauth_p == uaKrb4 ||
201 *userauth_p == uaKrb5))
208 * If this record isn't for our database, or this is the wrong
209 * sort of connection, ignore it.
212 if ((strcmp(db, database) != 0 && strcmp(db, "all") != 0) ||
213 raddr->sa.sa_family != AF_UNIX)
216 else if (strcmp(buf, "host") == 0)
218 struct in_addr file_ip_addr, mask;
220 /* Get the database. */
222 next_token(file, db, sizeof(db));
227 /* Read the IP address field. */
229 next_token(file, buf, sizeof(buf));
234 /* Remember the IP address field and go get mask field. */
236 if (!inet_aton(buf, &file_ip_addr))
238 read_through_eol(file);
242 /* Read the mask field. */
244 next_token(file, buf, sizeof(buf));
249 if (!inet_aton(buf, &mask))
251 read_through_eol(file);
256 * This is the record we're looking for. Read the rest of the
260 read_hba_entry2(file, userauth_p, auth_arg, error_p);
266 * If this record isn't for our database, or this is the wrong
267 * sort of connection, ignore it.
270 if ((strcmp(db, database) != 0 && strcmp(db, "all") != 0) ||
271 raddr->sa.sa_family != AF_INET ||
272 ((file_ip_addr.s_addr ^ raddr->in.sin_addr.s_addr) & mask.s_addr) != 0x0000)
277 read_through_eol(file);
287 "process_hba_record: invalid syntax in pg_hba.conf file\n");
289 fputs(PQerrormsg, stderr);
290 pqdebug("%s", PQerrormsg);
298 process_open_config_file(FILE *file, SockAddr *raddr, const char database[],
299 bool *host_ok_p, UserAuth * userauth_p,
302 /*---------------------------------------------------------------------------
303 This function does the same thing as find_hba_entry, only with
304 the config file already open on stream descriptor "file".
305 ----------------------------------------------------------------------------*/
308 /* We've processed a record that applies to our connection */
311 /* Said record has invalid syntax. */
312 bool eof; /* We've reached the end of the file we're
315 found_entry = false; /* initial value */
316 error = false; /* initial value */
317 eof = false; /* initial value */
318 while (!eof && !found_entry && !error)
320 /* Process a line from the config file */
322 int c; /* a character read from the file */
331 read_through_eol(file);
334 process_hba_record(file, raddr, database,
335 &found_entry, &error, userauth_p, auth_arg);
340 if (found_entry && !error)
347 find_hba_entry(SockAddr *raddr, const char database[], bool *host_ok_p,
348 UserAuth * userauth_p, char auth_arg[])
350 /*--------------------------------------------------------------------------
351 Read the config file and find an entry that allows connection from
352 host "*raddr" to database "database". If found, return *host_ok_p == true
353 and *userauth_p and *auth_arg representing the contents of that entry.
355 When a record has invalid syntax, we either ignore it or reject the
356 connection (depending on where it's invalid). No message or anything.
357 We need to fix that some day.
359 If we don't find or can't access the config file, we issue an error
360 message and deny the connection.
362 If we find a file by the old name of the config file (pg_hba), we issue
363 an error message because it probably needs to be converted. He didn't
364 follow directions and just installed his old hba file in the new database
367 ---------------------------------------------------------------------------*/
370 FILE *file; /* The config file we have to read */
374 /* The name of old config file that better not exist. */
376 /* Fail if config file by old name exists. */
379 /* put together the full pathname to the old config file */
380 old_conf_file = (char *) palloc((strlen(DataDir) +
381 strlen(OLD_CONF_FILE) + 2) * sizeof(char));
382 sprintf(old_conf_file, "%s/%s", DataDir, OLD_CONF_FILE);
384 if ((fd = open(old_conf_file, O_RDONLY, 0)) != -1)
386 /* Old config file exists. Tell this guy he needs to upgrade. */
389 "A file exists by the name used for host-based authentication "
390 "in prior releases of Postgres (%s). The name and format of "
391 "the configuration file have changed, so this file should be "
394 fputs(PQerrormsg, stderr);
395 pqdebug("%s", PQerrormsg);
399 char *conf_file; /* The name of the config file we have to
402 /* put together the full pathname to the config file */
403 conf_file = (char *) palloc((strlen(DataDir) +
404 strlen(CONF_FILE) + 2) * sizeof(char));
405 sprintf(conf_file, "%s/%s", DataDir, CONF_FILE);
407 file = AllocateFile(conf_file, "r");
410 /* The open of the config file failed. */
413 "find_hba_entry: Host-based authentication config file "
414 "does not exist or permissions are not setup correctly! "
415 "Unable to open file \"%s\".\n",
417 fputs(PQerrormsg, stderr);
418 pqdebug("%s", PQerrormsg);
422 process_open_config_file(file, raddr, database, host_ok_p, userauth_p,
428 pfree(old_conf_file);
434 interpret_ident_response(char ident_response[],
435 bool *error_p, char ident_username[])
437 /*----------------------------------------------------------------------------
438 Parse the string "ident_response[]" as a response from a query to an Ident
439 server. If it's a normal response indicating a username, return
440 *error_p == false and the username as ident_username[]. If it's anything
441 else, return *error_p == true and ident_username[] undefined.
442 ----------------------------------------------------------------------------*/
443 char *cursor; /* Cursor into ident_response[] */
445 cursor = &ident_response[0];
448 * Ident's response, in the telnet tradition, should end in crlf
451 if (strlen(ident_response) < 2)
453 else if (ident_response[strlen(ident_response) - 2] != '\r')
457 while (*cursor != ':' && *cursor != '\r')
458 cursor++; /* skip port field */
464 /* We're positioned to colon before response type field */
465 char response_type[80];
466 int i; /* Index into response_type[] */
468 cursor++; /* Go over colon */
469 while (isblank(*cursor))
470 cursor++; /* skip blanks */
472 while (*cursor != ':' && *cursor != '\r' && !isblank(*cursor)
473 && i < sizeof(response_type) - 1)
474 response_type[i++] = *cursor++;
475 response_type[i] = '\0';
476 while (isblank(*cursor))
477 cursor++; /* skip blanks */
478 if (strcmp(response_type, "USERID") != 0)
484 * It's a USERID response. Good. "cursor" should be
485 * pointing to the colon that precedes the operating
492 cursor++; /* Go over colon */
493 /* Skip over operating system field. */
494 while (*cursor != ':' && *cursor != '\r')
500 int i; /* Index into ident_username[] */
502 cursor++; /* Go over colon */
503 while (isblank(*cursor))
504 cursor++; /* skip blanks */
505 /* Rest of line is username. Copy it over. */
507 while (*cursor != '\r' && i < IDENT_USERNAME_MAX)
508 ident_username[i++] = *cursor++;
509 ident_username[i] = '\0';
521 ident(const struct in_addr remote_ip_addr, const struct in_addr local_ip_addr,
522 const ushort remote_port, const ushort local_port,
523 bool *ident_failed, char ident_username[])
525 /*--------------------------------------------------------------------------
526 Talk to the ident server on host "remote_ip_addr" and find out who
527 owns the tcp connection from his port "remote_port" to port
528 "local_port_addr" on host "local_ip_addr". Return the username the
529 ident server gives as "ident_username[]".
531 IP addresses and port numbers are in network byte order.
533 But iff we're unable to get the information from ident, return
534 *ident_failed == true (and ident_username[] undefined).
535 ----------------------------------------------------------------------------*/
539 /* File descriptor for socket on which we talk to Ident */
541 int rc; /* Return code from a locally called
544 sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
548 "Failed to create socket on which to talk to Ident server. "
549 "socket() returned errno = %s (%d)\n",
550 strerror(errno), errno);
551 fputs(PQerrormsg, stderr);
552 pqdebug("%s", PQerrormsg);
556 struct sockaddr_in ident_server;
559 * Socket address of Ident server on the system from which client
560 * is attempting to connect to us.
562 ident_server.sin_family = AF_INET;
563 ident_server.sin_port = htons(IDENT_PORT);
564 ident_server.sin_addr = remote_ip_addr;
565 rc = connect(sock_fd,
566 (struct sockaddr *) & ident_server, sizeof(ident_server));
570 "Unable to connect to Ident server on the host which is "
571 "trying to connect to Postgres "
572 "(IP address %s, Port %d). "
574 inet_ntoa(remote_ip_addr), IDENT_PORT, strerror(errno), errno);
575 fputs(PQerrormsg, stderr);
576 pqdebug("%s", PQerrormsg);
577 *ident_failed = true;
581 char ident_query[80];
583 /* The query we send to the Ident server */
584 sprintf(ident_query, "%d,%d\n",
585 ntohs(remote_port), ntohs(local_port));
586 rc = send(sock_fd, ident_query, strlen(ident_query), 0);
590 "Unable to send query to Ident server on the host which is "
591 "trying to connect to Postgres (Host %s, Port %d),"
592 "even though we successfully connected to it. "
594 inet_ntoa(remote_ip_addr), IDENT_PORT, strerror(errno), errno);
595 fputs(PQerrormsg, stderr);
596 pqdebug("%s", PQerrormsg);
597 *ident_failed = true;
601 char ident_response[80 + IDENT_USERNAME_MAX];
603 rc = recv(sock_fd, ident_response, sizeof(ident_response) - 1, 0);
607 "Unable to receive response from Ident server "
608 "on the host which is "
609 "trying to connect to Postgres (Host %s, Port %d),"
610 "even though we successfully sent our query to it. "
612 inet_ntoa(remote_ip_addr), IDENT_PORT,
613 strerror(errno), errno);
614 fputs(PQerrormsg, stderr);
615 pqdebug("%s", PQerrormsg);
616 *ident_failed = true;
620 bool error; /* response from Ident is garbage. */
622 ident_response[rc] = '\0';
623 interpret_ident_response(ident_response, &error, ident_username);
624 *ident_failed = error;
635 parse_map_record(FILE *file,
636 char file_map[], char file_pguser[], char file_iuser[])
638 /*---------------------------------------------------------------------------
639 Take the noncomment line which is next on file "file" and interpret
640 it as a line in a usermap file. Specifically, return the first
641 3 tokens as file_map, file_iuser, and file_pguser, respectively. If
642 there are fewer than 3 tokens, return null strings for the missing
645 ---------------------------------------------------------------------------*/
648 /* A token read from the file */
650 /* Set defaults in case fields not in file */
652 file_pguser[0] = '\0';
653 file_iuser[0] = '\0';
655 next_token(file, buf, sizeof(buf));
658 strcpy(file_map, buf);
659 next_token(file, buf, sizeof(buf));
662 strcpy(file_iuser, buf);
663 next_token(file, buf, sizeof(buf));
666 strcpy(file_pguser, buf);
667 read_through_eol(file);
676 verify_against_open_usermap(FILE *file,
678 const char ident_username[],
679 const char usermap_name[],
682 /*--------------------------------------------------------------------------
683 This function does the same thing as verify_against_usermap,
684 only with the config file already open on stream descriptor "file".
685 ---------------------------------------------------------------------------*/
686 bool match; /* We found a matching entry in the map
688 bool eof; /* We've reached the end of the file we're
691 match = false; /* initial value */
692 eof = false; /* initial value */
693 while (!eof && !match)
695 /* Process a line from the map file */
697 int c; /* a character read from the file */
706 read_through_eol(file);
709 /* The following are fields read from a record of the file */
710 char file_map[MAX_TOKEN + 1];
711 char file_pguser[MAX_TOKEN + 1];
712 char file_iuser[MAX_TOKEN + 1];
714 parse_map_record(file, file_map, file_pguser, file_iuser);
715 if (strcmp(file_map, usermap_name) == 0 &&
716 strcmp(file_pguser, pguser) == 0 &&
717 strcmp(file_iuser, ident_username) == 0)
722 *checks_out_p = match;
728 verify_against_usermap(const char pguser[],
729 const char ident_username[],
730 const char usermap_name[],
733 /*--------------------------------------------------------------------------
734 See if the user with ident username "ident_username" is allowed to act
735 as Postgres user "pguser" according to usermap "usermap_name". Look
736 it up in the usermap file.
738 Special case: For usermap "sameuser", don't look in the usermap
739 file. That's an implied map where "pguser" must be identical to
740 "ident_username" in order to be authorized.
742 Iff authorized, return *checks_out_p == true.
744 --------------------------------------------------------------------------*/
746 if (usermap_name[0] == '\0')
748 *checks_out_p = false;
750 "verify_against_usermap: hba configuration file does not "
751 "have the usermap field filled in in the entry that pertains "
752 "to this connection. That field is essential for Ident-based "
753 "authentication.\n");
754 fputs(PQerrormsg, stderr);
755 pqdebug("%s", PQerrormsg);
757 else if (strcmp(usermap_name, "sameuser") == 0)
759 if (strcmp(ident_username, pguser) == 0)
760 *checks_out_p = true;
762 *checks_out_p = false;
766 FILE *file; /* The map file we have to read */
768 char *map_file; /* The name of the map file we have to
771 /* put together the full pathname to the map file */
772 map_file = (char *) palloc((strlen(DataDir) +
773 strlen(MAP_FILE) + 2) * sizeof(char));
774 sprintf(map_file, "%s/%s", DataDir, MAP_FILE);
776 file = AllocateFile(map_file, "r");
779 /* The open of the map file failed. */
781 *checks_out_p = false;
784 "verify_against_usermap: usermap file for Ident-based "
786 "does not exist or permissions are not setup correctly! "
787 "Unable to open file \"%s\".\n",
789 fputs(PQerrormsg, stderr);
790 pqdebug("%s", PQerrormsg);
794 verify_against_open_usermap(file,
795 pguser, ident_username, usermap_name,
808 authident(struct sockaddr_in *raddr, struct sockaddr_in *laddr,
809 const char postgres_username[],
810 const char auth_arg[])
812 /*---------------------------------------------------------------------------
813 Talk to the ident server on the remote host and find out who owns the
814 connection described by "port". Then look in the usermap file under
815 the usermap auth_arg[] and see if that user is equivalent to
816 Postgres user user[].
818 Return STATUS_OK if yes.
819 ---------------------------------------------------------------------------*/
823 /* We were unable to get ident to give us a username */
824 char ident_username[IDENT_USERNAME_MAX + 1];
826 /* The username returned by ident */
828 ident(raddr->sin_addr, laddr->sin_addr,
829 raddr->sin_port, laddr->sin_port,
830 &ident_failed, ident_username);
835 verify_against_usermap(postgres_username, ident_username, auth_arg,
838 return (checks_out ? STATUS_OK : STATUS_ERROR);
844 hba_getauthmethod(SockAddr *raddr, char *database, char *auth_arg,
845 UserAuth *auth_method)
847 /*---------------------------------------------------------------------------
848 Determine what authentication method should be used when accessing database
849 "database" from frontend "raddr". Return the method, an optional argument,
851 ----------------------------------------------------------------------------*/
856 find_hba_entry(raddr, database, &host_ok, auth_method, auth_arg);
858 return (host_ok ? STATUS_OK : STATUS_ERROR);