1 /*-------------------------------------------------------------------------
4 * The front-end (client) authorization routines
6 * Copyright (c) 1994, Regents of the University of California
8 * NOTE: the error message strings returned by this module must not
9 * exceed INITIAL_EXPBUFFER_SIZE (currently 256 bytes).
12 * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-auth.c,v 1.33 1999/08/31 01:37:36 tgl Exp $
14 *-------------------------------------------------------------------------
19 * frontend (client) routines:
20 * fe_sendauth send authentication information
21 * fe_getauthname get user's name according to the client side
22 * of the authentication system
23 * fe_setauthsvc set frontend authentication service
24 * fe_getauthsvc get current frontend authentication service
32 #include "libpq-int.h"
39 #include <sys/param.h> /* for MAXHOSTNAMELEN on most */
40 #ifndef MAXHOSTNAMELEN
41 #include <netdb.h> /* for MAXHOSTNAMELEN on some */
51 /*----------------------------------------------------------------
52 * common definitions for generic fe/be routines
53 *----------------------------------------------------------------
58 char name[NAMEDATALEN]; /* service nickname (for command
60 MsgType msgtype; /* startup packet header type */
61 int allowed; /* initially allowed (before command line
66 * Command-line parsing routines use this structure to map nicknames
67 * onto service types (and the startup packets to use with them).
69 * Programs receiving an authentication request use this structure to
70 * decide which authentication service types are currently permitted.
71 * By default, all authentication systems compiled into the system are
72 * allowed. Unauthenticated connections are disallowed unless there
73 * isn't any authentication system.
75 static struct authsvc authsvcs[] = {
77 {"krb4", STARTUP_KRB4_MSG, 1},
78 {"kerberos", STARTUP_KRB4_MSG, 1},
81 {"krb5", STARTUP_KRB5_MSG, 1},
82 {"kerberos", STARTUP_KRB5_MSG, 1},
84 {UNAUTHNAME, STARTUP_MSG,
85 #if defined(KRB4) || defined(KRB5)
87 #else /* !(KRB4 || KRB5) */
89 #endif /* !(KRB4 || KRB5) */
91 {"password", STARTUP_PASSWORD_MSG, 0}
94 static int n_authsvcs = sizeof(authsvcs) / sizeof(struct authsvc);
97 /*----------------------------------------------------------------
98 * MIT Kerberos authentication system - protocol version 4
99 *----------------------------------------------------------------
104 /* for some reason, this is not defined in krb.h ... */
105 extern char *tkt_string(void);
108 * pg_krb4_init -- initialization performed before any Kerberos calls are made
110 * For v4, all we need to do is make sure the library routines get the right
111 * ticket file if we want them to see a special one. (They will open the file
118 static init_done = 0;
125 * If the user set PGREALM, then we use a ticket file with a special
126 * name: <usual-ticket-file-name>@<PGREALM-value>
128 if (realm = getenv("PGREALM"))
130 char tktbuf[MAXPATHLEN];
132 (void) sprintf(tktbuf, "%s@%s", tkt_string(), realm);
133 krb_set_tkt_string(tktbuf);
138 * pg_krb4_authname -- returns a pointer to static space containing whatever
139 * name the user has authenticated to the system
141 * We obtain this information by digging around in the ticket file.
144 pg_krb4_authname(char *PQerrormsg)
146 char instance[INST_SZ + 1];
147 char realm[REALM_SZ + 1];
149 static char name[SNAME_SZ + 1] = "";
156 name[SNAME_SZ] = '\0';
157 status = krb_get_tf_fullname(tkt_string(), name, instance, realm);
158 if (status != KSUCCESS)
160 (void) sprintf(PQerrormsg,
161 "pg_krb4_authname: krb_get_tf_fullname: %s\n",
162 krb_err_txt[status]);
163 return (char *) NULL;
169 * pg_krb4_sendauth -- client routine to send authentication information to
172 * This routine does not do mutual authentication, nor does it return enough
173 * information to do encrypted connections. But then, if we want to do
174 * encrypted connections, we'll have to redesign the whole RPC mechanism
177 * If the user is too lazy to feed us a hostname, we try to come up with
178 * something other than "localhost" since the hostname is used as an
179 * instance and instance names in v4 databases are usually actual hostnames
180 * (canonicalized to omit all domain suffixes).
183 pg_krb4_sendauth(const char *PQerrormsg, int sock,
184 struct sockaddr_in * laddr,
185 struct sockaddr_in * raddr,
186 const char *hostname)
188 long krbopts = 0; /* one-way authentication */
191 char hostbuf[MAXHOSTNAMELEN];
192 const char *realm = getenv("PGREALM"); /* NULL == current realm */
194 if (!hostname || !(*hostname))
196 if (gethostname(hostbuf, MAXHOSTNAMELEN) < 0)
197 strcpy(hostbuf, "localhost");
203 status = krb_sendauth(krbopts,
211 (CREDENTIALS *) NULL,
212 (Key_schedule *) NULL,
216 if (status != KSUCCESS)
218 (void) sprintf(PQerrormsg,
219 "pg_krb4_sendauth: kerberos error: %s\n",
220 krb_err_txt[status]);
229 /*----------------------------------------------------------------
230 * MIT Kerberos authentication system - protocol version 5
231 *----------------------------------------------------------------
234 #include "krb5/krb5.h"
237 * pg_an_to_ln -- return the local name corresponding to an authentication
240 * XXX Assumes that the first aname component is the user name. This is NOT
241 * necessarily so, since an aname can actually be something out of your
242 * worst X.400 nightmare, like
243 * ORGANIZATION=U. C. Berkeley/NAME=Paul M. Aoki@CS.BERKELEY.EDU
244 * Note that the MIT an_to_ln code does the same thing if you don't
245 * provide an aname mapping database...it may be a better idea to use
246 * krb5_an_to_ln, except that it punts if multiple components are found,
247 * and we can't afford to punt.
250 pg_an_to_ln(const char *aname)
254 if ((p = strchr(aname, '/')) || (p = strchr(aname, '@')))
261 * pg_krb5_init -- initialization performed before any Kerberos calls are made
263 * With v5, we can no longer set the ticket (credential cache) file name;
264 * we now have to provide a file handle for the open (well, "resolved")
265 * ticket file everywhere.
272 krb5_error_code code;
275 char tktbuf[MAXPATHLEN];
276 static krb5_ccache ccache = (krb5_ccache) NULL;
282 * If the user set PGREALM, then we use a ticket file with a special
283 * name: <usual-ticket-file-name>@<PGREALM-value>
285 if (!(defname = krb5_cc_default_name()))
287 (void) sprintf(PQerrormsg,
288 "pg_krb5_init: krb5_cc_default_name failed\n");
289 return (krb5_ccache) NULL;
291 strcpy(tktbuf, defname);
292 if (realm = getenv("PGREALM"))
295 strcat(tktbuf, realm);
298 if (code = krb5_cc_resolve(tktbuf, &ccache))
300 (void) sprintf(PQerrormsg,
301 "pg_krb5_init: Kerberos error %d in krb5_cc_resolve\n", code);
302 com_err("pg_krb5_init", code, "in krb5_cc_resolve");
303 return (krb5_ccache) NULL;
309 * pg_krb5_authname -- returns a pointer to static space containing whatever
310 * name the user has authenticated to the system
312 * We obtain this information by digging around in the ticket file.
315 pg_krb5_authname(const char *PQerrormsg)
318 krb5_principal principal;
319 krb5_error_code code;
320 static char *authname = (char *) NULL;
325 ccache = pg_krb5_init(); /* don't free this */
327 if (code = krb5_cc_get_principal(ccache, &principal))
329 (void) sprintf(PQerrormsg,
330 "pg_krb5_authname: Kerberos error %d in krb5_cc_get_principal\n", code);
331 com_err("pg_krb5_authname", code, "in krb5_cc_get_principal");
332 return (char *) NULL;
334 if (code = krb5_unparse_name(principal, &authname))
336 (void) sprintf(PQerrormsg,
337 "pg_krb5_authname: Kerberos error %d in krb5_unparse_name\n", code);
338 com_err("pg_krb5_authname", code, "in krb5_unparse_name");
339 krb5_free_principal(principal);
340 return (char *) NULL;
342 krb5_free_principal(principal);
343 return pg_an_to_ln(authname);
347 * pg_krb5_sendauth -- client routine to send authentication information to
350 * This routine does not do mutual authentication, nor does it return enough
351 * information to do encrypted connections. But then, if we want to do
352 * encrypted connections, we'll have to redesign the whole RPC mechanism
355 * Server hostnames are canonicalized v4-style, i.e., all domain suffixes
356 * are simply chopped off. Hence, we are assuming that you've entered your
357 * server instances as
358 * <value-of-PG_KRB_SRVNAM>/<canonicalized-hostname>
359 * in the PGREALM (or local) database. This is probably a bad assumption.
362 pg_krb5_sendauth(const char *PQerrormsg, int sock,
363 struct sockaddr_in * laddr,
364 struct sockaddr_in * raddr,
365 const char *hostname)
367 char servbuf[MAXHOSTNAMELEN + 1 +
368 sizeof(PG_KRB_SRVNAM)];
371 krb5_error_code code;
372 krb5_principal client,
375 krb5_error *error = (krb5_error *) NULL;
377 ccache = pg_krb5_init(); /* don't free this */
380 * set up client -- this is easy, we can get it out of the ticket
383 if (code = krb5_cc_get_principal(ccache, &client))
385 (void) sprintf(PQerrormsg,
386 "pg_krb5_sendauth: Kerberos error %d in krb5_cc_get_principal\n", code);
387 com_err("pg_krb5_sendauth", code, "in krb5_cc_get_principal");
392 * set up server -- canonicalize as described above
394 strcpy(servbuf, PG_KRB_SRVNAM);
395 *(hostp = servbuf + (sizeof(PG_KRB_SRVNAM) - 1)) = '/';
396 if (hostname || *hostname)
397 strncpy(++hostp, hostname, MAXHOSTNAMELEN);
400 if (gethostname(++hostp, MAXHOSTNAMELEN) < 0)
401 strcpy(hostp, "localhost");
403 if (hostp = strchr(hostp, '.'))
405 if (realm = getenv("PGREALM"))
407 strcat(servbuf, "@");
408 strcat(servbuf, realm);
410 if (code = krb5_parse_name(servbuf, &server))
412 (void) sprintf(PQerrormsg,
413 "pg_krb5_sendauth: Kerberos error %d in krb5_parse_name\n", code);
414 com_err("pg_krb5_sendauth", code, "in krb5_parse_name");
415 krb5_free_principal(client);
420 * The only thing we want back from krb5_sendauth is an error status
421 * and any error messages.
423 if (code = krb5_sendauth((krb5_pointer) & sock,
428 (krb5_checksum *) NULL,
432 (krb5_keyblock **) NULL,
434 (krb5_ap_rep_enc_part **) NULL))
436 if ((code == KRB5_SENDAUTH_REJECTED) && error)
438 (void) sprintf(PQerrormsg,
439 "pg_krb5_sendauth: authentication rejected: \"%*s\"\n",
440 error->text.length, error->text.data);
444 (void) sprintf(PQerrormsg,
445 "pg_krb5_sendauth: Kerberos error %d in krb5_sendauth\n", code);
446 com_err("pg_krb5_sendauth", code, "in krb5_sendauth");
449 krb5_free_principal(client);
450 krb5_free_principal(server);
451 return code ? STATUS_ERROR : STATUS_OK;
457 pg_password_sendauth(PGconn *conn, const char *password, AuthRequest areq)
459 /* Encrypt the password if needed. */
461 if (areq == AUTH_REQ_CRYPT)
462 password = crypt(password, conn->salt);
464 return pqPacketSend(conn, password, strlen(password) + 1);
468 * fe_sendauth -- client demux routine for outgoing authentication information
471 fe_sendauth(AuthRequest areq, PGconn *conn, const char *hostname,
472 const char *password, char *PQerrormsg)
481 if (pg_krb4_sendauth(PQerrormsg, conn->sock, &conn->laddr.in,
483 hostname) != STATUS_OK)
485 (void) sprintf(PQerrormsg,
486 "fe_sendauth: krb4 authentication failed\n");
491 (void) sprintf(PQerrormsg,
492 "fe_sendauth: krb4 authentication not supported\n");
498 if (pg_krb5_sendauth(PQerrormsg, conn->sock, &conn->laddr.in,
500 hostname) != STATUS_OK)
502 (void) sprintf(PQerrormsg,
503 "fe_sendauth: krb5 authentication failed\n");
508 (void) sprintf(PQerrormsg,
509 "fe_sendauth: krb5 authentication not supported\n");
513 case AUTH_REQ_PASSWORD:
515 if (password == NULL || *password == '\0')
517 (void) sprintf(PQerrormsg,
518 "fe_sendauth: no password supplied\n");
521 if (pg_password_sendauth(conn, password, areq) != STATUS_OK)
523 (void) sprintf(PQerrormsg,
524 "fe_sendauth: error sending password authentication\n");
531 (void) sprintf(PQerrormsg,
532 "fe_sendauth: authentication type %u not supported\n", areq);
543 * Set/return the authentication service currently selected for use by the
544 * frontend. (You can only use one in the frontend, obviously.)
546 static int pg_authsvc = -1;
549 fe_setauthsvc(const char *name, char *PQerrormsg)
553 for (i = 0; i < n_authsvcs; ++i)
554 if (!strcmp(name, authsvcs[i].name))
561 (void) sprintf(PQerrormsg,
562 "fe_setauthsvc: invalid name: %s, ignoring...\n",
569 fe_getauthsvc(char *PQerrormsg)
571 if (pg_authsvc < 0 || pg_authsvc >= n_authsvcs)
572 fe_setauthsvc(DEFAULT_CLIENT_AUTHSVC, PQerrormsg);
573 return authsvcs[pg_authsvc].msgtype;
577 * fe_getauthname -- returns a pointer to dynamic space containing whatever
578 * name the user has authenticated to the system
579 * if there is an error, return the error message in PQerrormsg
582 fe_getauthname(char *PQerrormsg)
584 char *name = (char *) NULL;
585 char *authn = (char *) NULL;
588 authsvc = fe_getauthsvc(PQerrormsg);
589 switch ((int) authsvc)
592 case STARTUP_KRB4_MSG:
593 name = pg_krb4_authname(PQerrormsg);
597 case STARTUP_KRB5_MSG:
598 name = pg_krb5_authname(PQerrormsg);
605 DWORD namesize = sizeof(username) - 1;
607 if (GetUserName(username, &namesize))
610 struct passwd *pw = getpwuid(geteuid());
618 (void) sprintf(PQerrormsg,
619 "fe_getauthname: invalid authentication system: %d\n",
624 if (name && (authn = (char *) malloc(strlen(name) + 1)))