]> granicus.if.org Git - postgresql/blob - src/backend/libpq/hba.c
3390f38f9a6a666a453734eead5284cbd841ebf0
[postgresql] / src / backend / libpq / hba.c
1 /*-------------------------------------------------------------------------
2  *
3  * hba.c--
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).
7  *
8  *
9  * IDENTIFICATION
10  *        $Header: /cvsroot/pgsql/src/backend/libpq/hba.c,v 1.25 1997/12/09 03:10:38 scrappy Exp $
11  *
12  *-------------------------------------------------------------------------
13  */
14 #include <stdio.h>
15 #include <string.h>
16 #include <errno.h>
17 #include <pwd.h>
18 #include <sys/types.h>
19 #include <fcntl.h>
20 #include <sys/socket.h>
21 #include <netinet/in.h>
22 #include <arpa/inet.h>
23 #include <unistd.h>
24
25 #include <postgres.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>
32
33 /* Some standard C libraries, including GNU, have an isblank() function.
34    Others, including Solaris, do not.  So we have our own.
35 */
36 static bool
37 isblank(const char c)
38 {
39         return (c == ' ' || c == 9 /* tab */ );
40 }
41
42
43
44 static void
45 next_token(FILE *fp, char *buf, const int bufsz)
46 {
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 --------------------------------------------------------------------------*/
56         int                     c;
57         char       *eb = buf + (bufsz - 1);
58
59         /* Move over inital token-delimiting blanks */
60         while (isblank(c = getc(fp)));
61
62         if (c != '\n')
63         {
64
65                 /*
66                  * build a token in buf of next characters up to EOF, eol, or
67                  * blank.
68                  */
69                 while (c != EOF && c != '\n' && !isblank(c))
70                 {
71                         if (buf < eb)
72                                 *buf++ = c;
73                         c = getc(fp);
74
75                         /*
76                          * Put back the char right after the token (putting back EOF
77                          * is ok)
78                          */
79                 }
80                 ungetc(c, fp);
81         }
82         *buf = '\0';
83 }
84
85
86
87 static void
88 read_through_eol(FILE *file)
89 {
90         int                     c;
91
92         do
93                 c = getc(file);
94         while (c != '\n' && c != EOF);
95 }
96
97
98
99 static void
100 read_hba_entry2(FILE *file, enum Userauth * userauth_p, char usermap_name[],
101                           bool *error_p, bool *matches_p, bool find_password_entries)
102 {
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, usermap_name, and
106   *error_p.
107 ---------------------------------------------------------------------------*/
108         char            buf[MAX_TOKEN];
109
110         bool            userauth_valid;
111
112         /* Get authentication type token. */
113         next_token(file, buf, sizeof(buf));
114         userauth_valid = false;
115         if (buf[0] == '\0')
116         {
117                 *error_p = true;
118         }
119         else
120         {
121                 userauth_valid = true;
122                 if (strcmp(buf, "trust") == 0)
123                 {
124                         *userauth_p = Trust;
125                 }
126                 else if (strcmp(buf, "ident") == 0)
127                 {
128                         *userauth_p = Ident;
129                 }
130                 else if (strcmp(buf, "password") == 0)
131                 {
132                         *userauth_p = Password;
133                 }
134                 else
135                 {
136                         userauth_valid = false;
137                 }
138
139                 if ((find_password_entries && strcmp(buf, "password") == 0) ||
140                         (!find_password_entries && strcmp(buf, "password") != 0))
141                 {
142                         *matches_p = true;
143                 }
144                 else
145                 {
146                         *matches_p = false;
147                 }
148         }
149
150         if (!userauth_valid || !*matches_p || *error_p)
151         {
152                 if (!userauth_valid)
153                 {
154                         *error_p = true;
155                 }
156                 read_through_eol(file);
157         }
158         else
159         {
160                 /* Get the map name token, if any */
161                 next_token(file, buf, sizeof(buf));
162                 if (buf[0] == '\0')
163                 {
164                         *error_p = false;
165                         usermap_name[0] = '\0';
166                 }
167                 else
168                 {
169                         strncpy(usermap_name, buf, USERMAP_NAME_SIZE);
170                         next_token(file, buf, sizeof(buf));
171                         if (buf[0] != '\0')
172                         {
173                                 *error_p = true;
174                                 read_through_eol(file);
175                         }
176                         else
177                                 *error_p = false;
178                 }
179         }
180 }
181
182
183
184 static void
185 process_hba_record(FILE *file,
186                                    const struct in_addr ip_addr, const char database[],
187                                    bool *matches_p, bool *error_p,
188                                    enum Userauth * userauth_p, char usermap_name[],
189                                    bool find_password_entries)
190 {
191 /*---------------------------------------------------------------------------
192   Process the non-comment record in the config file that is next on the file.
193   See if it applies to a connection to a host with IP address "ip_addr"
194   to a database named "database[]".  If so, return *matches_p true
195   and *userauth_p and usermap_name[] as the values from the entry.
196   If not, return matches_p false.  If the record has a syntax error,
197   return *error_p true, after issuing a message to stderr.      If no error,
198   leave *error_p as it was.
199 ---------------------------------------------------------------------------*/
200         char            buf[MAX_TOKEN]; /* A token from the record */
201
202         /* Read the record type field */
203         next_token(file, buf, sizeof(buf));
204         if (buf[0] == '\0')
205                 *matches_p = false;
206         else
207         {
208                 /* if this isn't a "host" record, it can't match. */
209                 if (strcmp(buf, "host") != 0)
210                 {
211                         *matches_p = false;
212                         read_through_eol(file);
213                 }
214                 else
215                 {
216                         /* It's a "host" record.  Read the database name field. */
217                         next_token(file, buf, sizeof(buf));
218                         if (buf[0] == '\0')
219                                 *matches_p = false;
220                         else
221                         {
222                                 /* If this record isn't for our database, ignore it. */
223                                 if (strcmp(buf, database) != 0 && strcmp(buf, "all") != 0)
224                                 {
225                                         *matches_p = false;
226                                         read_through_eol(file);
227                                 }
228                                 else
229                                 {
230                                         /* Read the IP address field */
231                                         next_token(file, buf, sizeof(buf));
232                                         if (buf[0] == '\0')
233                                                 *matches_p = false;
234                                         else
235                                         {
236                                                 int                     valid;          /* Field is valid dotted
237                                                                                                  * decimal */
238
239                                                 /*
240                                                  * Remember the IP address field and go get mask
241                                                  * field
242                                                  */
243                                                 struct in_addr file_ip_addr;    /* IP address field
244                                                                                                                  * value */
245
246                                                 valid = inet_aton(buf, &file_ip_addr);
247                                                 if (!valid)
248                                                 {
249                                                         *matches_p = false;
250                                                         read_through_eol(file);
251                                                 }
252                                                 else
253                                                 {
254                                                         /* Read the mask field */
255                                                         next_token(file, buf, sizeof(buf));
256                                                         if (buf[0] == '\0')
257                                                                 *matches_p = false;
258                                                         else
259                                                         {
260                                                                 struct in_addr mask;
261
262                                                                 /*
263                                                                  * Got mask.  Now see if this record is
264                                                                  * for our host.
265                                                                  */
266                                                                 valid = inet_aton(buf, &mask);
267                                                                 if (!valid)
268                                                                 {
269                                                                         *matches_p = false;
270                                                                         read_through_eol(file);
271                                                                 }
272                                                                 else
273                                                                 {
274                                                                         if (((file_ip_addr.s_addr ^ ip_addr.s_addr) & mask.s_addr)
275                                                                                 != 0x0000)
276                                                                         {
277                                                                                 *matches_p = false;
278                                                                                 read_through_eol(file);
279                                                                         }
280                                                                         else
281                                                                         {
282
283                                                                                 /*
284                                                                                  * This is the record we're
285                                                                                  * looking for.  Read the rest of
286                                                                                  * the info from it.
287                                                                                  */
288                                                                                 read_hba_entry2(file, userauth_p, usermap_name,
289                                                                                                                 error_p, matches_p, find_password_entries);
290                                                                                 if (*error_p)
291                                                                                 {
292                                                                                         sprintf(PQerrormsg,
293                                                                                                         "process_hba_record: invalid syntax in "
294                                                                                                         "hba config file "
295                                                                                                         "for host record for IP address %s\n",
296                                                                                                 inet_ntoa(file_ip_addr));
297                                                                                         fputs(PQerrormsg, stderr);
298                                                                                         pqdebug("%s", PQerrormsg);
299                                                                                 }
300                                                                         }
301                                                                 }
302                                                         }
303                                                 }
304                                         }
305                                 }
306                         }
307                 }
308         }
309 }
310
311
312
313 static void
314 process_open_config_file(FILE *file,
315                                          const struct in_addr ip_addr, const char database[],
316                                                  bool *host_ok_p, enum Userauth * userauth_p,
317                                                  char usermap_name[], bool find_password_entries)
318 {
319 /*---------------------------------------------------------------------------
320   This function does the same thing as find_hba_entry, only with
321   the config file already open on stream descriptor "file".
322 ----------------------------------------------------------------------------*/
323         bool            found_entry;
324
325         /* We've processed a record that applies to our connection */
326         bool            error;
327
328         /* Said record has invalid syntax. */
329         bool            eof;                    /* We've reached the end of the file we're
330                                                                  * reading */
331
332         found_entry = false;            /* initial value */
333         error = false;                          /* initial value */
334         eof = false;                            /* initial value */
335         while (!eof && !found_entry && !error)
336         {
337                 /* Process a line from the config file */
338
339                 int                     c;                      /* a character read from the file */
340
341                 c = getc(file);
342                 ungetc(c, file);
343                 if (c == EOF)
344                         eof = true;
345                 else
346                 {
347                         if (c == '#')
348                                 read_through_eol(file);
349                         else
350                         {
351                                 process_hba_record(file, ip_addr, database,
352                                                   &found_entry, &error, userauth_p, usermap_name,
353                                                                    find_password_entries);
354                         }
355                 }
356         }
357         if (found_entry)
358         {
359                 if (error)
360                         *host_ok_p = false;
361                 else
362                         *host_ok_p = true;
363         }
364         else
365                 *host_ok_p = false;
366 }
367
368
369
370 void
371 find_hba_entry(const char DataDir[], const struct in_addr ip_addr,
372                            const char database[],
373                            bool *host_ok_p, enum Userauth * userauth_p,
374                            char usermap_name[], bool find_password_entries)
375 {
376 /*--------------------------------------------------------------------------
377   Read the config file and find an entry that allows connection from
378   host "ip_addr" to database "database".  If not found, return
379   *host_ok_p == false.  If found, return *userauth_p and *usermap_name
380   representing the contents of that entry.
381
382   When a record has invalid syntax, we either ignore it or reject the
383   connection (depending on where it's invalid).  No message or anything.
384   We need to fix that some day.
385
386   If we don't find or can't access the config file, we issue an error
387   message and deny the connection.
388
389   If we find a file by the old name of the config file (pg_hba), we issue
390   an error message because it probably needs to be converted.  He didn't
391   follow directions and just installed his old hba file in the new database
392   system.
393
394 ---------------------------------------------------------------------------*/
395         int                     fd;
396
397         FILE       *file;                       /* The config file we have to read */
398
399         char       *old_conf_file;
400
401         /* The name of old config file that better not exist. */
402
403         /* Fail if config file by old name exists. */
404
405
406         /* put together the full pathname to the old config file */
407         old_conf_file = (char *) palloc((strlen(DataDir) +
408                                                           strlen(OLD_CONF_FILE) + 2) * sizeof(char));
409         sprintf(old_conf_file, "%s/%s", DataDir, OLD_CONF_FILE);
410
411         if ((fd = open(old_conf_file, O_RDONLY, 0)) != -1)
412         {
413                 /* Old config file exists.      Tell this guy he needs to upgrade. */
414                 close(fd);
415                 sprintf(PQerrormsg,
416                   "A file exists by the name used for host-based authentication "
417                    "in prior releases of Postgres (%s).  The name and format of "
418                    "the configuration file have changed, so this file should be "
419                                 "converted.\n",
420                                 old_conf_file);
421                 fputs(PQerrormsg, stderr);
422                 pqdebug("%s", PQerrormsg);
423         }
424         else
425         {
426                 char       *conf_file;  /* The name of the config file we have to
427                                                                  * read */
428
429                 /* put together the full pathname to the config file */
430                 conf_file = (char *) palloc((strlen(DataDir) +
431                                                                   strlen(CONF_FILE) + 2) * sizeof(char));
432                 sprintf(conf_file, "%s/%s", DataDir, CONF_FILE);
433
434                 file = AllocateFile(conf_file, "r");
435                 if (file == NULL)
436                 {
437                         /* The open of the config file failed.  */
438
439                         *host_ok_p = false;
440
441                         sprintf(PQerrormsg,
442                                  "find_hba_entry: Host-based authentication config file "
443                                 "does not exist or permissions are not setup correctly! "
444                                         "Unable to open file \"%s\".\n",
445                                         conf_file);
446                         fputs(PQerrormsg, stderr);
447                         pqdebug("%s", PQerrormsg);
448                 }
449                 else
450                 {
451                         process_open_config_file(file, ip_addr, database, host_ok_p, userauth_p,
452                                                                          usermap_name, find_password_entries);
453                         FreeFile(file);
454                 }
455                 pfree(conf_file);
456         }
457         pfree(old_conf_file);
458         return;
459 }
460
461
462 static void
463 interpret_ident_response(char ident_response[],
464                                                  bool *error_p, char ident_username[])
465 {
466 /*----------------------------------------------------------------------------
467   Parse the string "ident_response[]" as a response from a query to an Ident
468   server.  If it's a normal response indicating a username, return
469   *error_p == false and the username as ident_username[].  If it's anything
470   else, return *error_p == true and ident_username[] undefined.
471 ----------------------------------------------------------------------------*/
472         char       *cursor;                     /* Cursor into ident_response[] */
473
474         cursor = &ident_response[0];
475
476         /*
477          * Ident's response, in the telnet tradition, should end in crlf
478          * (\r\n).
479          */
480         if (strlen(ident_response) < 2)
481                 *error_p = true;
482         else if (ident_response[strlen(ident_response) - 2] != '\r')
483                 *error_p = true;
484         else
485         {
486                 while (*cursor != ':' && *cursor != '\r')
487                         cursor++;                       /* skip port field */
488
489                 if (*cursor != ':')
490                         *error_p = true;
491                 else
492                 {
493                         /* We're positioned to colon before response type field */
494                         char            response_type[80];
495                         int                     i;              /* Index into response_type[] */
496
497                         cursor++;                       /* Go over colon */
498                         while (isblank(*cursor))
499                                 cursor++;               /* skip blanks */
500                         i = 0;
501                         while (*cursor != ':' && *cursor != '\r' && !isblank(*cursor)
502                                    && i < sizeof(response_type) - 1)
503                                 response_type[i++] = *cursor++;
504                         response_type[i] = '\0';
505                         while (isblank(*cursor))
506                                 cursor++;               /* skip blanks */
507                         if (strcmp(response_type, "USERID") != 0)
508                                 *error_p = true;
509                         else
510                         {
511
512                                 /*
513                                  * It's a USERID response.  Good.  "cursor" should be
514                                  * pointing to the colon that precedes the operating
515                                  * system type.
516                                  */
517                                 if (*cursor != ':')
518                                         *error_p = true;
519                                 else
520                                 {
521                                         cursor++;       /* Go over colon */
522                                         /* Skip over operating system field. */
523                                         while (*cursor != ':' && *cursor != '\r')
524                                                 cursor++;
525                                         if (*cursor != ':')
526                                                 *error_p = true;
527                                         else
528                                         {
529                                                 int                     i;      /* Index into ident_username[] */
530
531                                                 cursor++;               /* Go over colon */
532                                                 while (isblank(*cursor))
533                                                         cursor++;       /* skip blanks */
534                                                 /* Rest of line is username.  Copy it over. */
535                                                 i = 0;
536                                                 while (*cursor != '\r' && i < IDENT_USERNAME_MAX)
537                                                         ident_username[i++] = *cursor++;
538                                                 ident_username[i] = '\0';
539                                                 *error_p = false;
540                                         }
541                                 }
542                         }
543                 }
544         }
545 }
546
547
548
549 static void
550 ident(const struct in_addr remote_ip_addr, const struct in_addr local_ip_addr,
551           const ushort remote_port, const ushort local_port,
552           bool *ident_failed, char ident_username[])
553 {
554 /*--------------------------------------------------------------------------
555   Talk to the ident server on host "remote_ip_addr" and find out who
556   owns the tcp connection from his port "remote_port" to port
557   "local_port_addr" on host "local_ip_addr".  Return the username the
558   ident server gives as "ident_username[]".
559
560   IP addresses and port numbers are in network byte order.
561
562   But iff we're unable to get the information from ident, return
563   *ident_failed == true (and ident_username[] undefined).
564 ----------------------------------------------------------------------------*/
565
566         int                     sock_fd;
567
568         /* File descriptor for socket on which we talk to Ident */
569
570         int                     rc;                             /* Return code from a locally called
571                                                                  * function */
572
573         sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
574         if (sock_fd == -1)
575         {
576                 sprintf(PQerrormsg,
577                          "Failed to create socket on which to talk to Ident server. "
578                                 "socket() returned errno = %s (%d)\n",
579                                 strerror(errno), errno);
580                 fputs(PQerrormsg, stderr);
581                 pqdebug("%s", PQerrormsg);
582         }
583         else
584         {
585                 struct sockaddr_in ident_server;
586
587                 /*
588                  * Socket address of Ident server on the system from which client
589                  * is attempting to connect to us.
590                  */
591                 ident_server.sin_family = AF_INET;
592                 ident_server.sin_port = htons(IDENT_PORT);
593                 ident_server.sin_addr = remote_ip_addr;
594                 rc = connect(sock_fd,
595                            (struct sockaddr *) & ident_server, sizeof(ident_server));
596                 if (rc != 0)
597                 {
598                         sprintf(PQerrormsg,
599                                 "Unable to connect to Ident server on the host which is "
600                                         "trying to connect to Postgres "
601                                         "(IP address %s, Port %d). "
602                                         "errno = %s (%d)\n",
603                                         inet_ntoa(remote_ip_addr), IDENT_PORT, strerror(errno), errno);
604                         fputs(PQerrormsg, stderr);
605                         pqdebug("%s", PQerrormsg);
606                         *ident_failed = true;
607                 }
608                 else
609                 {
610                         char            ident_query[80];
611
612                         /* The query we send to the Ident server */
613                         sprintf(ident_query, "%d,%d\n",
614                                         ntohs(remote_port), ntohs(local_port));
615                         rc = send(sock_fd, ident_query, strlen(ident_query), 0);
616                         if (rc < 0)
617                         {
618                                 sprintf(PQerrormsg,
619                                                 "Unable to send query to Ident server on the host which is "
620                                           "trying to connect to Postgres (Host %s, Port %d),"
621                                                 "even though we successfully connected to it.  "
622                                                 "errno = %s (%d)\n",
623                                                 inet_ntoa(remote_ip_addr), IDENT_PORT, strerror(errno), errno);
624                                 fputs(PQerrormsg, stderr);
625                                 pqdebug("%s", PQerrormsg);
626                                 *ident_failed = true;
627                         }
628                         else
629                         {
630                                 char            ident_response[80 + IDENT_USERNAME_MAX];
631
632                                 rc = recv(sock_fd, ident_response, sizeof(ident_response) - 1, 0);
633                                 if (rc < 0)
634                                 {
635                                         sprintf(PQerrormsg,
636                                                   "Unable to receive response from Ident server "
637                                                         "on the host which is "
638                                           "trying to connect to Postgres (Host %s, Port %d),"
639                                         "even though we successfully sent our query to it.  "
640                                                         "errno = %s (%d)\n",
641                                                         inet_ntoa(remote_ip_addr), IDENT_PORT,
642                                                         strerror(errno), errno);
643                                         fputs(PQerrormsg, stderr);
644                                         pqdebug("%s", PQerrormsg);
645                                         *ident_failed = true;
646                                 }
647                                 else
648                                 {
649                                         bool            error;  /* response from Ident is garbage. */
650
651                                         ident_response[rc] = '\0';
652                                         interpret_ident_response(ident_response, &error, ident_username);
653                                         *ident_failed = error;
654                                 }
655                         }
656                         close(sock_fd);
657                 }
658         }
659 }
660
661
662
663 static void
664 parse_map_record(FILE *file,
665                                  char file_map[], char file_pguser[], char file_iuser[])
666 {
667 /*---------------------------------------------------------------------------
668   Take the noncomment line which is next on file "file" and interpret
669   it as a line in a usermap file.  Specifically, return the first
670   3 tokens as file_map, file_iuser, and file_pguser, respectively.      If
671   there are fewer than 3 tokens, return null strings for the missing
672   ones.
673
674 ---------------------------------------------------------------------------*/
675         char            buf[MAX_TOKEN];
676
677         /* A token read from the file */
678
679         /* Set defaults in case fields not in file */
680         file_map[0] = '\0';
681         file_pguser[0] = '\0';
682         file_iuser[0] = '\0';
683
684         next_token(file, buf, sizeof(buf));
685         if (buf != '\0')
686         {
687                 strcpy(file_map, buf);
688                 next_token(file, buf, sizeof(buf));
689                 if (buf != '\0')
690                 {
691                         strcpy(file_iuser, buf);
692                         next_token(file, buf, sizeof(buf));
693                         if (buf != '\0')
694                         {
695                                 strcpy(file_pguser, buf);
696                                 read_through_eol(file);
697                         }
698                 }
699         }
700 }
701
702
703
704 static void
705 verify_against_open_usermap(FILE *file,
706                                                         const char pguser[],
707                                                         const char ident_username[],
708                                                         const char usermap_name[],
709                                                         bool *checks_out_p)
710 {
711 /*--------------------------------------------------------------------------
712   This function does the same thing as verify_against_usermap,
713   only with the config file already open on stream descriptor "file".
714 ---------------------------------------------------------------------------*/
715         bool            match;                  /* We found a matching entry in the map
716                                                                  * file */
717         bool            eof;                    /* We've reached the end of the file we're
718                                                                  * reading */
719
720         match = false;                          /* initial value */
721         eof = false;                            /* initial value */
722         while (!eof && !match)
723         {
724                 /* Process a line from the map file */
725
726                 int                     c;                      /* a character read from the file */
727
728                 c = getc(file);
729                 ungetc(c, file);
730                 if (c == EOF)
731                         eof = true;
732                 else
733                 {
734                         if (c == '#')
735                                 read_through_eol(file);
736                         else
737                         {
738                                 /* The following are fields read from a record of the file */
739                                 char            file_map[MAX_TOKEN + 1];
740                                 char            file_pguser[MAX_TOKEN + 1];
741                                 char            file_iuser[MAX_TOKEN + 1];
742
743                                 parse_map_record(file, file_map, file_pguser, file_iuser);
744                                 if (strcmp(file_map, usermap_name) == 0 &&
745                                         strcmp(file_pguser, pguser) == 0 &&
746                                         strcmp(file_iuser, ident_username) == 0)
747                                         match = true;
748                         }
749                 }
750         }
751         *checks_out_p = match;
752 }
753
754
755
756 static void
757 verify_against_usermap(const char DataDir[],
758                                            const char pguser[],
759                                            const char ident_username[],
760                                            const char usermap_name[],
761                                            bool *checks_out_p)
762 {
763 /*--------------------------------------------------------------------------
764   See if the user with ident username "ident_username" is allowed to act
765   as Postgres user "pguser" according to usermap "usermap_name".   Look
766   it up in the usermap file.
767
768   Special case: For usermap "sameuser", don't look in the usermap
769   file.  That's an implied map where "pguser" must be identical to
770   "ident_username" in order to be authorized.
771
772   Iff authorized, return *checks_out_p == true.
773
774 --------------------------------------------------------------------------*/
775
776         if (usermap_name[0] == '\0')
777         {
778                 *checks_out_p = false;
779                 sprintf(PQerrormsg,
780                                 "verify_against_usermap: hba configuration file does not "
781                    "have the usermap field filled in in the entry that pertains "
782                   "to this connection.  That field is essential for Ident-based "
783                                 "authentication.\n");
784                 fputs(PQerrormsg, stderr);
785                 pqdebug("%s", PQerrormsg);
786         }
787         else if (strcmp(usermap_name, "sameuser") == 0)
788         {
789                 if (strcmp(ident_username, pguser) == 0)
790                         *checks_out_p = true;
791                 else
792                         *checks_out_p = false;
793         }
794         else
795         {
796                 FILE       *file;               /* The map file we have to read */
797
798                 char       *map_file;   /* The name of the map file we have to
799                                                                  * read */
800
801                 /* put together the full pathname to the map file */
802                 map_file = (char *) palloc((strlen(DataDir) +
803                                                                         strlen(MAP_FILE) + 2) * sizeof(char));
804                 sprintf(map_file, "%s/%s", DataDir, MAP_FILE);
805
806                 file = AllocateFile(map_file, "r");
807                 if (file == NULL)
808                 {
809                         /* The open of the map file failed.  */
810
811                         *checks_out_p = false;
812
813                         sprintf(PQerrormsg,
814                                   "verify_against_usermap: usermap file for Ident-based "
815                                         "authentication "
816                                 "does not exist or permissions are not setup correctly! "
817                                         "Unable to open file \"%s\".\n",
818                                         map_file);
819                         fputs(PQerrormsg, stderr);
820                         pqdebug("%s", PQerrormsg);
821                 }
822                 else
823                 {
824                         verify_against_open_usermap(file,
825                                                                         pguser, ident_username, usermap_name,
826                                                                                 checks_out_p);
827                         FreeFile(file);
828                 }
829                 pfree(map_file);
830
831
832         }
833 }
834
835
836
837 static void
838 authident(const char DataDir[],
839                   const Port port, const char postgres_username[],
840                   const char usermap_name[],
841                   bool *authentic_p)
842 {
843 /*---------------------------------------------------------------------------
844   Talk to the ident server on the remote host and find out who owns the
845   connection described by "port".  Then look in the usermap file under
846   the usermap usermap_name[] and see if that user is equivalent to
847   Postgres user user[].
848
849   Return *authentic_p true iff yes.
850 ---------------------------------------------------------------------------*/
851         bool            ident_failed;
852
853         /* We were unable to get ident to give us a username */
854         char            ident_username[IDENT_USERNAME_MAX + 1];
855
856         /* The username returned by ident */
857
858         ident(port.raddr.in.sin_addr, port.laddr.in.sin_addr,
859                   port.raddr.in.sin_port, port.laddr.in.sin_port,
860                   &ident_failed, ident_username);
861
862         if (ident_failed)
863                 *authentic_p = false;
864         else
865         {
866                 bool            checks_out;
867
868                 verify_against_usermap(DataDir,
869                                                  postgres_username, ident_username, usermap_name,
870                                                            &checks_out);
871                 if (checks_out)
872                         *authentic_p = true;
873                 else
874                         *authentic_p = false;
875         }
876 }
877
878
879
880 extern int
881 hba_recvauth(const Port *port, const char database[], const char user[],
882                          const char DataDir[])
883 {
884 /*---------------------------------------------------------------------------
885   Determine if the TCP connection described by "port" is with someone
886   allowed to act as user "user" and access database "database".  Return
887   STATUS_OK if yes; STATUS_ERROR if not.
888 ----------------------------------------------------------------------------*/
889         bool            host_ok;
890
891         /*
892          * There's an entry for this database and remote host in the pg_hba
893          * file
894          */
895         char            usermap_name[USERMAP_NAME_SIZE + 1];
896
897         /*
898          * The name of the map pg_hba specifies for this connection (or
899          * special value "SAMEUSER")
900          */
901         enum Userauth userauth;
902
903         /*
904          * The type of user authentication pg_hba specifies for this
905          * connection
906          */
907         int                     retvalue;
908
909         /* UNIX socket always OK, for now */
910         if (port->raddr.in.sin_family == AF_UNIX)
911                 return STATUS_OK;
912         /* Our eventual return value */
913
914
915         find_hba_entry(DataDir, port->raddr.in.sin_addr, database,
916                                    &host_ok, &userauth, usermap_name,
917                                    false                /* don't find password entries of type
918                                            'password' */ );
919
920         if (!host_ok)
921                 retvalue = STATUS_ERROR;
922         else
923         {
924                 switch (userauth)
925                 {
926                         case Trust:
927                                 retvalue = STATUS_OK;
928                                 break;
929                                 case Ident:
930                                 {
931
932                                         /*
933                                          * Here's where we need to call up ident and
934                                          * authenticate the user
935                                          */
936
937                                         bool            authentic;              /* He is who he says he
938                                                                                                  * is. */
939
940                                                                 authident(DataDir, *port, user, usermap_name, &authentic);
941
942                                         if                      (authentic)
943                                                                         retvalue = STATUS_OK;
944                                         else
945                                                                         retvalue = STATUS_ERROR;
946                                 }
947                                                         break;
948
949                         default:
950                                 retvalue = STATUS_ERROR;
951                                 Assert(false);
952                 }
953         }
954         return (retvalue);
955 }
956
957
958 /*----------------------------------------------------------------
959  * This version of hba was written by Bryan Henderson
960  * in September 1996 for Release 6.0.  It changed the format of the
961  * hba file and added ident function.
962  *
963  * Here are some notes about the original host based authentication
964  * the preceded this one.
965  *
966  * based on the securelib package originally written by William
967  * LeFebvre, EECS Department, Northwestern University
968  * (phil@eecs.nwu.edu) - orginal configuration file code handling
969  * by Sam Horrocks (sam@ics.uci.edu)
970  *
971  * modified and adapted for use with Postgres95 by Paul Fisher
972  * (pnfisher@unity.ncsu.edu)
973  *
974  -----------------------------------------------------------------*/