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.35 2000/01/18 19:05:31 momjian 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
34 #include "libpq-int.h"
41 #include <sys/param.h> /* for MAXHOSTNAMELEN on most */
42 #ifndef MAXHOSTNAMELEN
43 #include <netdb.h> /* for MAXHOSTNAMELEN on some */
53 /*----------------------------------------------------------------
54 * common definitions for generic fe/be routines
55 *----------------------------------------------------------------
60 char name[NAMEDATALEN]; /* service nickname (for command
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 int 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[MAXPGPATH];
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 + 1];
149 char realm[REALM_SZ + 1];
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]);
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[MAXPGPATH];
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", code);
304 com_err("pg_krb5_init", code, "in krb5_cc_resolve");
305 return (krb5_ccache) NULL;
311 * pg_krb5_authname -- returns a pointer to static space containing whatever
312 * name the user has authenticated to the system
314 * We obtain this information by digging around in the ticket file.
317 pg_krb5_authname(const char *PQerrormsg)
320 krb5_principal principal;
321 krb5_error_code code;
322 static char *authname = (char *) NULL;
327 ccache = pg_krb5_init(); /* don't free this */
329 if (code = krb5_cc_get_principal(ccache, &principal))
331 (void) sprintf(PQerrormsg,
332 "pg_krb5_authname: Kerberos error %d in krb5_cc_get_principal\n", code);
333 com_err("pg_krb5_authname", code, "in krb5_cc_get_principal");
334 return (char *) NULL;
336 if (code = krb5_unparse_name(principal, &authname))
338 (void) sprintf(PQerrormsg,
339 "pg_krb5_authname: Kerberos error %d in krb5_unparse_name\n", code);
340 com_err("pg_krb5_authname", code, "in krb5_unparse_name");
341 krb5_free_principal(principal);
342 return (char *) NULL;
344 krb5_free_principal(principal);
345 return pg_an_to_ln(authname);
349 * pg_krb5_sendauth -- client routine to send authentication information to
352 * This routine does not do mutual authentication, nor does it return enough
353 * information to do encrypted connections. But then, if we want to do
354 * encrypted connections, we'll have to redesign the whole RPC mechanism
357 * Server hostnames are canonicalized v4-style, i.e., all domain suffixes
358 * are simply chopped off. Hence, we are assuming that you've entered your
359 * server instances as
360 * <value-of-PG_KRB_SRVNAM>/<canonicalized-hostname>
361 * in the PGREALM (or local) database. This is probably a bad assumption.
364 pg_krb5_sendauth(const char *PQerrormsg, int sock,
365 struct sockaddr_in * laddr,
366 struct sockaddr_in * raddr,
367 const char *hostname)
369 char servbuf[MAXHOSTNAMELEN + 1 +
370 sizeof(PG_KRB_SRVNAM)];
373 krb5_error_code code;
374 krb5_principal client,
377 krb5_error *error = (krb5_error *) NULL;
379 ccache = pg_krb5_init(); /* don't free this */
382 * set up client -- this is easy, we can get it out of the ticket
385 if (code = krb5_cc_get_principal(ccache, &client))
387 (void) sprintf(PQerrormsg,
388 "pg_krb5_sendauth: Kerberos error %d in krb5_cc_get_principal\n", code);
389 com_err("pg_krb5_sendauth", code, "in krb5_cc_get_principal");
394 * set up server -- canonicalize as described above
396 strcpy(servbuf, PG_KRB_SRVNAM);
397 *(hostp = servbuf + (sizeof(PG_KRB_SRVNAM) - 1)) = '/';
398 if (hostname || *hostname)
399 strncpy(++hostp, hostname, MAXHOSTNAMELEN);
402 if (gethostname(++hostp, MAXHOSTNAMELEN) < 0)
403 strcpy(hostp, "localhost");
405 if (hostp = strchr(hostp, '.'))
407 if (realm = getenv("PGREALM"))
409 strcat(servbuf, "@");
410 strcat(servbuf, realm);
412 if (code = krb5_parse_name(servbuf, &server))
414 (void) sprintf(PQerrormsg,
415 "pg_krb5_sendauth: Kerberos error %d in krb5_parse_name\n", code);
416 com_err("pg_krb5_sendauth", code, "in krb5_parse_name");
417 krb5_free_principal(client);
422 * The only thing we want back from krb5_sendauth is an error status
423 * and any error messages.
425 if (code = krb5_sendauth((krb5_pointer) & sock,
430 (krb5_checksum *) NULL,
434 (krb5_keyblock **) NULL,
436 (krb5_ap_rep_enc_part **) NULL))
438 if ((code == KRB5_SENDAUTH_REJECTED) && error)
440 (void) sprintf(PQerrormsg,
441 "pg_krb5_sendauth: authentication rejected: \"%*s\"\n",
442 error->text.length, error->text.data);
446 (void) sprintf(PQerrormsg,
447 "pg_krb5_sendauth: Kerberos error %d in krb5_sendauth\n", code);
448 com_err("pg_krb5_sendauth", code, "in krb5_sendauth");
451 krb5_free_principal(client);
452 krb5_free_principal(server);
453 return code ? STATUS_ERROR : STATUS_OK;
459 pg_password_sendauth(PGconn *conn, const char *password, AuthRequest areq)
461 /* Encrypt the password if needed. */
463 if (areq == AUTH_REQ_CRYPT)
464 password = crypt(password, conn->salt);
466 return pqPacketSend(conn, password, strlen(password) + 1);
470 * fe_sendauth -- client demux routine for outgoing authentication information
473 fe_sendauth(AuthRequest areq, PGconn *conn, const char *hostname,
474 const char *password, char *PQerrormsg)
483 if (pg_krb4_sendauth(PQerrormsg, conn->sock, &conn->laddr.in,
485 hostname) != STATUS_OK)
487 (void) sprintf(PQerrormsg,
488 "fe_sendauth: krb4 authentication failed\n");
493 (void) sprintf(PQerrormsg,
494 "fe_sendauth: krb4 authentication not supported\n");
500 if (pg_krb5_sendauth(PQerrormsg, conn->sock, &conn->laddr.in,
502 hostname) != STATUS_OK)
504 (void) sprintf(PQerrormsg,
505 "fe_sendauth: krb5 authentication failed\n");
510 (void) sprintf(PQerrormsg,
511 "fe_sendauth: krb5 authentication not supported\n");
515 case AUTH_REQ_PASSWORD:
517 if (password == NULL || *password == '\0')
519 (void) sprintf(PQerrormsg,
520 "fe_sendauth: no password supplied\n");
523 if (pg_password_sendauth(conn, password, areq) != STATUS_OK)
525 (void) sprintf(PQerrormsg,
526 "fe_sendauth: error sending password authentication\n");
533 (void) sprintf(PQerrormsg,
534 "fe_sendauth: authentication type %u not supported\n", areq);
545 * Set/return the authentication service currently selected for use by the
546 * frontend. (You can only use one in the frontend, obviously.)
548 static int pg_authsvc = -1;
551 fe_setauthsvc(const char *name, char *PQerrormsg)
555 for (i = 0; i < n_authsvcs; ++i)
556 if (!strcmp(name, authsvcs[i].name))
563 (void) sprintf(PQerrormsg,
564 "fe_setauthsvc: invalid name: %s, ignoring...\n",
571 fe_getauthsvc(char *PQerrormsg)
573 if (pg_authsvc < 0 || pg_authsvc >= n_authsvcs)
574 fe_setauthsvc(DEFAULT_CLIENT_AUTHSVC, PQerrormsg);
575 return authsvcs[pg_authsvc].msgtype;
579 * fe_getauthname -- returns a pointer to dynamic space containing whatever
580 * name the user has authenticated to the system
581 * if there is an error, return the error message in PQerrormsg
584 fe_getauthname(char *PQerrormsg)
586 char *name = (char *) NULL;
587 char *authn = (char *) NULL;
590 authsvc = fe_getauthsvc(PQerrormsg);
591 switch ((int) authsvc)
594 case STARTUP_KRB4_MSG:
595 name = pg_krb4_authname(PQerrormsg);
599 case STARTUP_KRB5_MSG:
600 name = pg_krb5_authname(PQerrormsg);
607 DWORD namesize = sizeof(username) - 1;
609 if (GetUserName(username, &namesize))
612 struct passwd *pw = getpwuid(geteuid());
620 (void) sprintf(PQerrormsg,
621 "fe_getauthname: invalid authentication system: %d\n",
626 if (name && (authn = (char *) malloc(strlen(name) + 1)))