1 /*-------------------------------------------------------------------------
4 * The front-end (client) authorization routines
6 * Copyright (c) 1994, Regents of the University of California
10 * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-auth.c,v 1.21 1998/08/09 02:59:25 momjian Exp $
12 *-------------------------------------------------------------------------
17 * frontend (client) routines:
18 * fe_sendauth send authentication information
19 * fe_getauthname get user's name according to the client side
20 * of the authentication system
21 * fe_setauthsvc set frontend authentication service
22 * fe_getauthsvc get current frontend authentication service
32 #include <sys/param.h> /* for MAXHOSTNAMELEN on most */
33 #include <sys/types.h>
34 #ifndef MAXHOSTNAMELEN
35 #include <netdb.h> /* for MAXHOSTNAMELEN on some */
43 #include "libpq/pqcomm.h"
47 #include "fe-connect.h"
54 /*----------------------------------------------------------------
55 * common definitions for generic fe/be routines
56 *----------------------------------------------------------------
61 char name[NAMEDATALEN]; /* service nickname (for command line) */
62 MsgType msgtype; /* startup packet header type */
63 int allowed; /* initially allowed (before command line
68 * Command-line parsing routines use this structure to map nicknames
69 * onto service types (and the startup packets to use with them).
71 * Programs receiving an authentication request use this structure to
72 * decide which authentication service types are currently permitted.
73 * By default, all authentication systems compiled into the system are
74 * allowed. Unauthenticated connections are disallowed unless there
75 * isn't any authentication system.
77 static struct authsvc authsvcs[] = {
79 {"krb4", STARTUP_KRB4_MSG, 1},
80 {"kerberos", STARTUP_KRB4_MSG, 1},
83 {"krb5", STARTUP_KRB5_MSG, 1},
84 {"kerberos", STARTUP_KRB5_MSG, 1},
86 {UNAUTHNAME, STARTUP_MSG,
87 #if defined(KRB4) || defined(KRB5)
89 #else /* !(KRB4 || KRB5) */
91 #endif /* !(KRB4 || KRB5) */
93 {"password", STARTUP_PASSWORD_MSG, 0}
96 static n_authsvcs = sizeof(authsvcs) / sizeof(struct authsvc);
99 /*----------------------------------------------------------------
100 * MIT Kerberos authentication system - protocol version 4
101 *----------------------------------------------------------------
106 /* for some reason, this is not defined in krb.h ... */
107 extern char *tkt_string(void);
110 * pg_krb4_init -- initialization performed before any Kerberos calls are made
112 * For v4, all we need to do is make sure the library routines get the right
113 * ticket file if we want them to see a special one. (They will open the file
120 static init_done = 0;
127 * If the user set PGREALM, then we use a ticket file with a special
128 * name: <usual-ticket-file-name>@<PGREALM-value>
130 if (realm = getenv("PGREALM"))
132 char tktbuf[MAXPATHLEN];
134 (void) sprintf(tktbuf, "%s@%s", tkt_string(), realm);
135 krb_set_tkt_string(tktbuf);
140 * pg_krb4_authname -- returns a pointer to static space containing whatever
141 * name the user has authenticated to the system
143 * We obtain this information by digging around in the ticket file.
146 pg_krb4_authname(char *PQerrormsg)
148 char instance[INST_SZ];
149 char realm[REALM_SZ];
151 static char name[SNAME_SZ + 1] = "";
158 name[SNAME_SZ] = '\0';
159 status = krb_get_tf_fullname(tkt_string(), name, instance, realm);
160 if (status != KSUCCESS)
162 (void) sprintf(PQerrormsg,
163 "pg_krb4_authname: krb_get_tf_fullname: %s\n",
164 krb_err_txt[status]);
165 return ((char *) NULL);
171 * pg_krb4_sendauth -- client routine to send authentication information to
174 * This routine does not do mutual authentication, nor does it return enough
175 * information to do encrypted connections. But then, if we want to do
176 * encrypted connections, we'll have to redesign the whole RPC mechanism
179 * If the user is too lazy to feed us a hostname, we try to come up with
180 * something other than "localhost" since the hostname is used as an
181 * instance and instance names in v4 databases are usually actual hostnames
182 * (canonicalized to omit all domain suffixes).
185 pg_krb4_sendauth(const char *PQerrormsg, int sock,
186 struct sockaddr_in * laddr,
187 struct sockaddr_in * raddr,
188 const char *hostname)
190 long krbopts = 0; /* one-way authentication */
193 char hostbuf[MAXHOSTNAMELEN];
194 const char *realm = getenv("PGREALM"); /* NULL == current realm */
196 if (!hostname || !(*hostname))
198 if (gethostname(hostbuf, MAXHOSTNAMELEN) < 0)
199 strcpy(hostbuf, "localhost");
205 status = krb_sendauth(krbopts,
213 (CREDENTIALS *) NULL,
214 (Key_schedule *) NULL,
218 if (status != KSUCCESS)
220 (void) sprintf(PQerrormsg,
221 "pg_krb4_sendauth: kerberos error: %s\n",
222 krb_err_txt[status]);
223 return (STATUS_ERROR);
231 /*----------------------------------------------------------------
232 * MIT Kerberos authentication system - protocol version 5
233 *----------------------------------------------------------------
236 #include "krb5/krb5.h"
239 * pg_an_to_ln -- return the local name corresponding to an authentication
242 * XXX Assumes that the first aname component is the user name. This is NOT
243 * necessarily so, since an aname can actually be something out of your
244 * worst X.400 nightmare, like
245 * ORGANIZATION=U. C. Berkeley/NAME=Paul M. Aoki@CS.BERKELEY.EDU
246 * Note that the MIT an_to_ln code does the same thing if you don't
247 * provide an aname mapping database...it may be a better idea to use
248 * krb5_an_to_ln, except that it punts if multiple components are found,
249 * and we can't afford to punt.
252 pg_an_to_ln(const char *aname)
256 if ((p = strchr(aname, '/')) || (p = strchr(aname, '@')))
263 * pg_krb5_init -- initialization performed before any Kerberos calls are made
265 * With v5, we can no longer set the ticket (credential cache) file name;
266 * we now have to provide a file handle for the open (well, "resolved")
267 * ticket file everywhere.
274 krb5_error_code code;
277 char tktbuf[MAXPATHLEN];
278 static krb5_ccache ccache = (krb5_ccache) NULL;
284 * If the user set PGREALM, then we use a ticket file with a special
285 * name: <usual-ticket-file-name>@<PGREALM-value>
287 if (!(defname = krb5_cc_default_name()))
289 (void) sprintf(PQerrormsg,
290 "pg_krb5_init: krb5_cc_default_name failed\n");
291 return ((krb5_ccache) NULL);
293 strcpy(tktbuf, defname);
294 if (realm = getenv("PGREALM"))
297 strcat(tktbuf, realm);
300 if (code = krb5_cc_resolve(tktbuf, &ccache))
302 (void) sprintf(PQerrormsg,
303 "pg_krb5_init: Kerberos error %d in krb5_cc_resolve\n",
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",
335 com_err("pg_krb5_authname", code, "in krb5_cc_get_principal");
336 return ((char *) NULL);
338 if (code = krb5_unparse_name(principal, &authname))
340 (void) sprintf(PQerrormsg,
341 "pg_krb5_authname: Kerberos error %d in krb5_unparse_name\n",
343 com_err("pg_krb5_authname", code, "in krb5_unparse_name");
344 krb5_free_principal(principal);
345 return ((char *) NULL);
347 krb5_free_principal(principal);
348 return (pg_an_to_ln(authname));
352 * pg_krb5_sendauth -- client routine to send authentication information to
355 * This routine does not do mutual authentication, nor does it return enough
356 * information to do encrypted connections. But then, if we want to do
357 * encrypted connections, we'll have to redesign the whole RPC mechanism
360 * Server hostnames are canonicalized v4-style, i.e., all domain suffixes
361 * are simply chopped off. Hence, we are assuming that you've entered your
362 * server instances as
363 * <value-of-PG_KRB_SRVNAM>/<canonicalized-hostname>
364 * in the PGREALM (or local) database. This is probably a bad assumption.
367 pg_krb5_sendauth(const char *PQerrormsg, int sock,
368 struct sockaddr_in * laddr,
369 struct sockaddr_in * raddr,
370 const char *hostname)
372 char servbuf[MAXHOSTNAMELEN + 1 +
373 sizeof(PG_KRB_SRVNAM)];
376 krb5_error_code code;
377 krb5_principal client,
380 krb5_error *error = (krb5_error *) NULL;
382 ccache = pg_krb5_init(); /* don't free this */
385 * set up client -- this is easy, we can get it out of the ticket
388 if (code = krb5_cc_get_principal(ccache, &client))
390 (void) sprintf(PQerrormsg,
391 "pg_krb5_sendauth: Kerberos error %d in krb5_cc_get_principal\n",
393 com_err("pg_krb5_sendauth", code, "in krb5_cc_get_principal");
394 return (STATUS_ERROR);
398 * set up server -- canonicalize as described above
400 strcpy(servbuf, PG_KRB_SRVNAM);
401 *(hostp = servbuf + (sizeof(PG_KRB_SRVNAM) - 1)) = '/';
402 if (hostname || *hostname)
403 strncpy(++hostp, hostname, MAXHOSTNAMELEN);
406 if (gethostname(++hostp, MAXHOSTNAMELEN) < 0)
407 strcpy(hostp, "localhost");
409 if (hostp = strchr(hostp, '.'))
411 if (realm = getenv("PGREALM"))
413 strcat(servbuf, "@");
414 strcat(servbuf, realm);
416 if (code = krb5_parse_name(servbuf, &server))
418 (void) sprintf(PQerrormsg,
419 "pg_krb5_sendauth: Kerberos error %d in krb5_parse_name\n",
421 com_err("pg_krb5_sendauth", code, "in krb5_parse_name");
422 krb5_free_principal(client);
423 return (STATUS_ERROR);
427 * The only thing we want back from krb5_sendauth is an error status
428 * and any error messages.
430 if (code = krb5_sendauth((krb5_pointer) & sock,
435 (krb5_checksum *) NULL,
439 (krb5_keyblock **) NULL,
441 (krb5_ap_rep_enc_part **) NULL))
443 if ((code == KRB5_SENDAUTH_REJECTED) && error)
445 (void) sprintf(PQerrormsg,
446 "pg_krb5_sendauth: authentication rejected: \"%*s\"\n",
447 error->text.length, error->text.data);
451 (void) sprintf(PQerrormsg,
452 "pg_krb5_sendauth: Kerberos error %d in krb5_sendauth\n",
454 com_err("pg_krb5_sendauth", code, "in krb5_sendauth");
457 krb5_free_principal(client);
458 krb5_free_principal(server);
459 return (code ? STATUS_ERROR : STATUS_OK);
465 pg_password_sendauth(PGconn *conn, const char *password, AuthRequest areq)
467 /* Encrypt the password if needed. */
469 if (areq == AUTH_REQ_CRYPT)
470 password = crypt(password, conn->salt);
472 return packetSend(conn, password, strlen(password) + 1);
476 * fe_sendauth -- client demux routine for outgoing authentication information
479 fe_sendauth(AuthRequest areq, PGconn *conn, const char *hostname,
480 const char *password, char *PQerrormsg)
489 if (pg_krb4_sendauth(PQerrormsg, conn->sock, &conn->laddr.in,
491 hostname) != STATUS_OK)
493 (void) sprintf(PQerrormsg,
494 "fe_sendauth: krb4 authentication failed\n");
495 return (STATUS_ERROR);
499 (void) sprintf(PQerrormsg,
500 "fe_sendauth: krb4 authentication not supported\n");
501 return (STATUS_ERROR);
506 if (pg_krb5_sendauth(PQerrormsg, conn->sock, &conn->laddr.in,
508 hostname) != STATUS_OK)
510 (void) sprintf(PQerrormsg,
511 "fe_sendauth: krb5 authentication failed\n");
512 return (STATUS_ERROR);
516 (void) sprintf(PQerrormsg,
517 "fe_sendauth: krb5 authentication not supported\n");
518 return (STATUS_ERROR);
521 case AUTH_REQ_PASSWORD:
523 if (password == NULL || *password == '\0')
525 (void) sprintf(PQerrormsg,
526 "fe_sendauth: no password supplied\n");
527 return (STATUS_ERROR);
529 if (pg_password_sendauth(conn, password, areq) != STATUS_OK)
531 (void) sprintf(PQerrormsg,
532 "fe_sendauth: error sending password authentication\n");
533 return (STATUS_ERROR);
539 (void) sprintf(PQerrormsg,
540 "fe_sendauth: authentication type %u not supported\n", areq);
541 return (STATUS_ERROR);
551 * Set/return the authentication service currently selected for use by the
552 * frontend. (You can only use one in the frontend, obviously.)
554 static pg_authsvc = -1;
557 fe_setauthsvc(const char *name, char *PQerrormsg)
561 for (i = 0; i < n_authsvcs; ++i)
562 if (!strcmp(name, authsvcs[i].name))
569 (void) sprintf(PQerrormsg,
570 "fe_setauthsvc: invalid name: %s, ignoring...\n",
577 fe_getauthsvc(char *PQerrormsg)
579 if (pg_authsvc < 0 || pg_authsvc >= n_authsvcs)
580 fe_setauthsvc(DEFAULT_CLIENT_AUTHSVC, PQerrormsg);
581 return (authsvcs[pg_authsvc].msgtype);
585 * fe_getauthname -- returns a pointer to dynamic space containing whatever
586 * name the user has authenticated to the system
587 * if there is an error, return the error message in PQerrormsg
590 fe_getauthname(char *PQerrormsg)
592 char *name = (char *) NULL;
593 char *authn = (char *) NULL;
596 authsvc = fe_getauthsvc(PQerrormsg);
597 switch ((int) authsvc)
600 case STARTUP_KRB4_MSG:
601 name = pg_krb4_authname(PQerrormsg);
605 case STARTUP_KRB5_MSG:
606 name = pg_krb5_authname(PQerrormsg);
613 DWORD namesize = sizeof(username) - 1;
615 if (GetUserName(username,&namesize))
618 struct passwd *pw = getpwuid(geteuid());
626 (void) sprintf(PQerrormsg,
627 "fe_getauthname: invalid authentication system: %d\n",
632 if (name && (authn = (char *) malloc(strlen(name) + 1)))