1 /*-------------------------------------------------------------------------
4 * The front-end (client) authorization routines
6 * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
7 * Portions Copyright (c) 1994, Regents of the University of California
9 * NOTE: the error message strings returned by this module must not
10 * exceed INITIAL_EXPBUFFER_SIZE (currently 256 bytes).
13 * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-auth.c,v 1.41 2000/05/27 03:58:20 momjian Exp $
15 *-------------------------------------------------------------------------
20 * frontend (client) routines:
21 * fe_sendauth send authentication information
22 * fe_getauthname get user's name according to the client side
23 * of the authentication system
24 * fe_setauthsvc set frontend authentication service
25 * fe_getauthsvc get current frontend authentication service
35 #include "libpq-int.h"
42 #include <sys/param.h> /* for MAXHOSTNAMELEN on most */
43 #ifndef MAXHOSTNAMELEN
44 #include <netdb.h> /* for MAXHOSTNAMELEN on some */
54 /*----------------------------------------------------------------
55 * common definitions for generic fe/be routines
56 *----------------------------------------------------------------
61 char name[NAMEDATALEN]; /* service nickname (for command
63 MsgType msgtype; /* startup packet header type */
64 int allowed; /* initially allowed (before command line
69 * Command-line parsing routines use this structure to map nicknames
70 * onto service types (and the startup packets to use with them).
72 * Programs receiving an authentication request use this structure to
73 * decide which authentication service types are currently permitted.
74 * By default, all authentication systems compiled into the system are
75 * allowed. Unauthenticated connections are disallowed unless there
76 * isn't any authentication system.
78 static const struct authsvc authsvcs[] = {
80 {"krb4", STARTUP_KRB4_MSG, 1},
81 {"kerberos", STARTUP_KRB4_MSG, 1},
84 {"krb5", STARTUP_KRB5_MSG, 1},
85 {"kerberos", STARTUP_KRB5_MSG, 1},
87 {UNAUTHNAME, STARTUP_MSG,
88 #if defined(KRB4) || defined(KRB5)
90 #else /* !(KRB4 || KRB5) */
92 #endif /* !(KRB4 || KRB5) */
94 {"password", STARTUP_PASSWORD_MSG, 0}
97 static const int n_authsvcs = sizeof(authsvcs) / sizeof(struct authsvc);
100 /*----------------------------------------------------------------
101 * MIT Kerberos authentication system - protocol version 4
102 *----------------------------------------------------------------
107 /* for some reason, this is not defined in krb.h ... */
108 extern char *tkt_string(void);
111 * pg_krb4_init -- initialization performed before any Kerberos calls are made
113 * For v4, all we need to do is make sure the library routines get the right
114 * ticket file if we want them to see a special one. (They will open the file
121 static init_done = 0;
128 * If the user set PGREALM, then we use a ticket file with a special
129 * name: <usual-ticket-file-name>@<PGREALM-value>
131 if (realm = getenv("PGREALM"))
133 char tktbuf[MAXPGPATH];
135 (void) sprintf(tktbuf, "%s@%s", tkt_string(), realm);
136 krb_set_tkt_string(tktbuf);
141 * pg_krb4_authname -- returns a pointer to static space containing whatever
142 * name the user has authenticated to the system
144 * We obtain this information by digging around in the ticket file.
147 pg_krb4_authname(char *PQerrormsg)
149 char instance[INST_SZ + 1];
150 char realm[REALM_SZ + 1];
152 static char name[SNAME_SZ + 1] = "";
159 name[SNAME_SZ] = '\0';
160 status = krb_get_tf_fullname(tkt_string(), name, instance, realm);
161 if (status != KSUCCESS)
163 (void) sprintf(PQerrormsg,
164 "pg_krb4_authname: krb_get_tf_fullname: %s\n",
165 krb_err_txt[status]);
166 return (char *) NULL;
172 * pg_krb4_sendauth -- client routine to send authentication information to
175 * This routine does not do mutual authentication, nor does it return enough
176 * information to do encrypted connections. But then, if we want to do
177 * encrypted connections, we'll have to redesign the whole RPC mechanism
180 * If the user is too lazy to feed us a hostname, we try to come up with
181 * something other than "localhost" since the hostname is used as an
182 * instance and instance names in v4 databases are usually actual hostnames
183 * (canonicalized to omit all domain suffixes).
186 pg_krb4_sendauth(const char *PQerrormsg, int sock,
187 struct sockaddr_in * laddr,
188 struct sockaddr_in * raddr,
189 const char *hostname)
191 long krbopts = 0; /* one-way authentication */
194 char hostbuf[MAXHOSTNAMELEN];
195 const char *realm = getenv("PGREALM"); /* NULL == current realm */
197 if (!hostname || !(*hostname))
199 if (gethostname(hostbuf, MAXHOSTNAMELEN) < 0)
200 strcpy(hostbuf, "localhost");
206 status = krb_sendauth(krbopts,
214 (CREDENTIALS *) NULL,
215 (Key_schedule *) NULL,
219 if (status != KSUCCESS)
221 (void) sprintf(PQerrormsg,
222 "pg_krb4_sendauth: kerberos error: %s\n",
223 krb_err_txt[status]);
232 /*----------------------------------------------------------------
233 * MIT Kerberos authentication system - protocol version 5
234 *----------------------------------------------------------------
237 #include "krb5/krb5.h"
240 * pg_an_to_ln -- return the local name corresponding to an authentication
243 * XXX Assumes that the first aname component is the user name. This is NOT
244 * necessarily so, since an aname can actually be something out of your
245 * worst X.400 nightmare, like
246 * ORGANIZATION=U. C. Berkeley/NAME=Paul M. Aoki@CS.BERKELEY.EDU
247 * Note that the MIT an_to_ln code does the same thing if you don't
248 * provide an aname mapping database...it may be a better idea to use
249 * krb5_an_to_ln, except that it punts if multiple components are found,
250 * and we can't afford to punt.
253 pg_an_to_ln(const char *aname)
257 if ((p = strchr(aname, '/')) || (p = strchr(aname, '@')))
264 * pg_krb5_init -- initialization performed before any Kerberos calls are made
266 * With v5, we can no longer set the ticket (credential cache) file name;
267 * we now have to provide a file handle for the open (well, "resolved")
268 * ticket file everywhere.
275 krb5_error_code code;
278 char tktbuf[MAXPGPATH];
279 static krb5_ccache ccache = (krb5_ccache) NULL;
285 * If the user set PGREALM, then we use a ticket file with a special
286 * name: <usual-ticket-file-name>@<PGREALM-value>
288 if (!(defname = krb5_cc_default_name()))
290 (void) sprintf(PQerrormsg,
291 "pg_krb5_init: krb5_cc_default_name failed\n");
292 return (krb5_ccache) NULL;
294 strcpy(tktbuf, defname);
295 if (realm = getenv("PGREALM"))
298 strcat(tktbuf, realm);
301 if (code = krb5_cc_resolve(tktbuf, &ccache))
303 (void) sprintf(PQerrormsg,
304 "pg_krb5_init: Kerberos error %d in krb5_cc_resolve\n", code);
305 com_err("pg_krb5_init", code, "in krb5_cc_resolve");
306 return (krb5_ccache) NULL;
312 * pg_krb5_authname -- returns a pointer to static space containing whatever
313 * name the user has authenticated to the system
315 * We obtain this information by digging around in the ticket file.
318 pg_krb5_authname(const char *PQerrormsg)
321 krb5_principal principal;
322 krb5_error_code code;
323 static char *authname = (char *) NULL;
328 ccache = pg_krb5_init(); /* don't free this */
330 if (code = krb5_cc_get_principal(ccache, &principal))
332 (void) sprintf(PQerrormsg,
333 "pg_krb5_authname: Kerberos error %d in krb5_cc_get_principal\n", code);
334 com_err("pg_krb5_authname", code, "in krb5_cc_get_principal");
335 return (char *) NULL;
337 if (code = krb5_unparse_name(principal, &authname))
339 (void) sprintf(PQerrormsg,
340 "pg_krb5_authname: Kerberos error %d in krb5_unparse_name\n", code);
341 com_err("pg_krb5_authname", code, "in krb5_unparse_name");
342 krb5_free_principal(principal);
343 return (char *) NULL;
345 krb5_free_principal(principal);
346 return pg_an_to_ln(authname);
350 * pg_krb5_sendauth -- client routine to send authentication information to
353 * This routine does not do mutual authentication, nor does it return enough
354 * information to do encrypted connections. But then, if we want to do
355 * encrypted connections, we'll have to redesign the whole RPC mechanism
358 * Server hostnames are canonicalized v4-style, i.e., all domain suffixes
359 * are simply chopped off. Hence, we are assuming that you've entered your
360 * server instances as
361 * <value-of-PG_KRB_SRVNAM>/<canonicalized-hostname>
362 * in the PGREALM (or local) database. This is probably a bad assumption.
365 pg_krb5_sendauth(const char *PQerrormsg, int sock,
366 struct sockaddr_in * laddr,
367 struct sockaddr_in * raddr,
368 const char *hostname)
370 char servbuf[MAXHOSTNAMELEN + 1 +
371 sizeof(PG_KRB_SRVNAM)];
374 krb5_error_code code;
375 krb5_principal client,
378 krb5_error *error = (krb5_error *) NULL;
380 ccache = pg_krb5_init(); /* don't free this */
383 * set up client -- this is easy, we can get it out of the ticket
386 if (code = krb5_cc_get_principal(ccache, &client))
388 (void) sprintf(PQerrormsg,
389 "pg_krb5_sendauth: Kerberos error %d in krb5_cc_get_principal\n", code);
390 com_err("pg_krb5_sendauth", code, "in krb5_cc_get_principal");
395 * set up server -- canonicalize as described above
397 strcpy(servbuf, PG_KRB_SRVNAM);
398 *(hostp = servbuf + (sizeof(PG_KRB_SRVNAM) - 1)) = '/';
399 if (hostname || *hostname)
400 strncpy(++hostp, hostname, MAXHOSTNAMELEN);
403 if (gethostname(++hostp, MAXHOSTNAMELEN) < 0)
404 strcpy(hostp, "localhost");
406 if (hostp = strchr(hostp, '.'))
408 if (realm = getenv("PGREALM"))
410 strcat(servbuf, "@");
411 strcat(servbuf, realm);
413 if (code = krb5_parse_name(servbuf, &server))
415 (void) sprintf(PQerrormsg,
416 "pg_krb5_sendauth: Kerberos error %d in krb5_parse_name\n", code);
417 com_err("pg_krb5_sendauth", code, "in krb5_parse_name");
418 krb5_free_principal(client);
423 * The only thing we want back from krb5_sendauth is an error status
424 * and any error messages.
426 if (code = krb5_sendauth((krb5_pointer) & sock,
431 (krb5_checksum *) NULL,
435 (krb5_keyblock **) NULL,
437 (krb5_ap_rep_enc_part **) NULL))
439 if ((code == KRB5_SENDAUTH_REJECTED) && error)
441 (void) sprintf(PQerrormsg,
442 "pg_krb5_sendauth: authentication rejected: \"%*s\"\n",
443 error->text.length, error->text.data);
447 (void) sprintf(PQerrormsg,
448 "pg_krb5_sendauth: Kerberos error %d in krb5_sendauth\n", code);
449 com_err("pg_krb5_sendauth", code, "in krb5_sendauth");
452 krb5_free_principal(client);
453 krb5_free_principal(server);
454 return code ? STATUS_ERROR : STATUS_OK;
460 pg_password_sendauth(PGconn *conn, const char *password, AuthRequest areq)
462 /* Encrypt the password if needed. */
464 if (areq == AUTH_REQ_CRYPT)
465 password = crypt(password, conn->salt);
467 return pqPacketSend(conn, password, strlen(password) + 1);
471 * fe_sendauth -- client demux routine for outgoing authentication information
474 fe_sendauth(AuthRequest areq, PGconn *conn, const char *hostname,
475 const char *password, char *PQerrormsg)
477 #if !defined(KRB4) && !defined(KRB5)
478 (void) hostname; /* not used */
488 if (pg_krb4_sendauth(PQerrormsg, conn->sock, &conn->laddr.in,
490 hostname) != STATUS_OK)
492 (void) sprintf(PQerrormsg,
493 "fe_sendauth: krb4 authentication failed\n");
498 (void) sprintf(PQerrormsg,
499 "fe_sendauth: krb4 authentication not supported\n");
505 if (pg_krb5_sendauth(PQerrormsg, conn->sock, &conn->laddr.in,
507 hostname) != STATUS_OK)
509 (void) sprintf(PQerrormsg,
510 "fe_sendauth: krb5 authentication failed\n");
515 (void) sprintf(PQerrormsg,
516 "fe_sendauth: krb5 authentication not supported\n");
520 case AUTH_REQ_PASSWORD:
522 if (password == NULL || *password == '\0')
524 (void) sprintf(PQerrormsg,
525 "fe_sendauth: no password supplied\n");
528 if (pg_password_sendauth(conn, password, areq) != STATUS_OK)
530 (void) sprintf(PQerrormsg,
531 "fe_sendauth: error sending password authentication\n");
538 (void) sprintf(PQerrormsg,
539 "fe_sendauth: authentication type %u not supported\n", areq);
550 * Set/return the authentication service currently selected for use by the
551 * frontend. (You can only use one in the frontend, obviously.)
553 * NB: This is not thread-safe if different threads try to select different
554 * authentication services! It's OK for fe_getauthsvc to select the default,
555 * since that will be the same for all threads, but direct application use
556 * of fe_setauthsvc is not thread-safe. However, use of fe_setauthsvc is
557 * deprecated anyway...
560 static int pg_authsvc = -1;
563 fe_setauthsvc(const char *name, char *PQerrormsg)
567 for (i = 0; i < n_authsvcs; ++i)
568 if (strcmp(name, authsvcs[i].name) == 0)
575 (void) sprintf(PQerrormsg,
576 "fe_setauthsvc: invalid name: %s, ignoring...\n",
583 fe_getauthsvc(char *PQerrormsg)
585 if (pg_authsvc < 0 || pg_authsvc >= n_authsvcs)
586 fe_setauthsvc(DEFAULT_CLIENT_AUTHSVC, PQerrormsg);
587 return authsvcs[pg_authsvc].msgtype;
591 * fe_getauthname -- returns a pointer to dynamic space containing whatever
592 * name the user has authenticated to the system
593 * if there is an error, return the error message in PQerrormsg
596 fe_getauthname(char *PQerrormsg)
598 char *name = (char *) NULL;
599 char *authn = (char *) NULL;
602 authsvc = fe_getauthsvc(PQerrormsg);
603 switch ((int) authsvc)
606 case STARTUP_KRB4_MSG:
607 name = pg_krb4_authname(PQerrormsg);
611 case STARTUP_KRB5_MSG:
612 name = pg_krb5_authname(PQerrormsg);
619 DWORD namesize = sizeof(username) - 1;
621 if (GetUserName(username, &namesize))
624 struct passwd *pw = getpwuid(geteuid());
632 (void) sprintf(PQerrormsg,
633 "fe_getauthname: invalid authentication system: %d\n",
638 if (name && (authn = (char *) malloc(strlen(name) + 1)))