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.32 1999/07/19 06:25:38 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
30 #include "libpq-int.h"
37 #include <sys/param.h> /* for MAXHOSTNAMELEN on most */
38 #ifndef MAXHOSTNAMELEN
39 #include <netdb.h> /* for MAXHOSTNAMELEN on some */
49 /*----------------------------------------------------------------
50 * common definitions for generic fe/be routines
51 *----------------------------------------------------------------
56 char name[NAMEDATALEN]; /* service nickname (for command
58 MsgType msgtype; /* startup packet header type */
59 int allowed; /* initially allowed (before command line
64 * Command-line parsing routines use this structure to map nicknames
65 * onto service types (and the startup packets to use with them).
67 * Programs receiving an authentication request use this structure to
68 * decide which authentication service types are currently permitted.
69 * By default, all authentication systems compiled into the system are
70 * allowed. Unauthenticated connections are disallowed unless there
71 * isn't any authentication system.
73 static struct authsvc authsvcs[] = {
75 {"krb4", STARTUP_KRB4_MSG, 1},
76 {"kerberos", STARTUP_KRB4_MSG, 1},
79 {"krb5", STARTUP_KRB5_MSG, 1},
80 {"kerberos", STARTUP_KRB5_MSG, 1},
82 {UNAUTHNAME, STARTUP_MSG,
83 #if defined(KRB4) || defined(KRB5)
85 #else /* !(KRB4 || KRB5) */
87 #endif /* !(KRB4 || KRB5) */
89 {"password", STARTUP_PASSWORD_MSG, 0}
92 static int n_authsvcs = sizeof(authsvcs) / sizeof(struct authsvc);
95 /*----------------------------------------------------------------
96 * MIT Kerberos authentication system - protocol version 4
97 *----------------------------------------------------------------
102 /* for some reason, this is not defined in krb.h ... */
103 extern char *tkt_string(void);
106 * pg_krb4_init -- initialization performed before any Kerberos calls are made
108 * For v4, all we need to do is make sure the library routines get the right
109 * ticket file if we want them to see a special one. (They will open the file
116 static init_done = 0;
123 * If the user set PGREALM, then we use a ticket file with a special
124 * name: <usual-ticket-file-name>@<PGREALM-value>
126 if (realm = getenv("PGREALM"))
128 char tktbuf[MAXPATHLEN];
130 (void) sprintf(tktbuf, "%s@%s", tkt_string(), realm);
131 krb_set_tkt_string(tktbuf);
136 * pg_krb4_authname -- returns a pointer to static space containing whatever
137 * name the user has authenticated to the system
139 * We obtain this information by digging around in the ticket file.
142 pg_krb4_authname(char *PQerrormsg)
144 char instance[INST_SZ + 1];
145 char realm[REALM_SZ + 1];
147 static char name[SNAME_SZ + 1] = "";
154 name[SNAME_SZ] = '\0';
155 status = krb_get_tf_fullname(tkt_string(), name, instance, realm);
156 if (status != KSUCCESS)
158 (void) sprintf(PQerrormsg,
159 "pg_krb4_authname: krb_get_tf_fullname: %s\n",
160 krb_err_txt[status]);
161 return (char *) NULL;
167 * pg_krb4_sendauth -- client routine to send authentication information to
170 * This routine does not do mutual authentication, nor does it return enough
171 * information to do encrypted connections. But then, if we want to do
172 * encrypted connections, we'll have to redesign the whole RPC mechanism
175 * If the user is too lazy to feed us a hostname, we try to come up with
176 * something other than "localhost" since the hostname is used as an
177 * instance and instance names in v4 databases are usually actual hostnames
178 * (canonicalized to omit all domain suffixes).
181 pg_krb4_sendauth(const char *PQerrormsg, int sock,
182 struct sockaddr_in * laddr,
183 struct sockaddr_in * raddr,
184 const char *hostname)
186 long krbopts = 0; /* one-way authentication */
189 char hostbuf[MAXHOSTNAMELEN];
190 const char *realm = getenv("PGREALM"); /* NULL == current realm */
192 if (!hostname || !(*hostname))
194 if (gethostname(hostbuf, MAXHOSTNAMELEN) < 0)
195 strcpy(hostbuf, "localhost");
201 status = krb_sendauth(krbopts,
209 (CREDENTIALS *) NULL,
210 (Key_schedule *) NULL,
214 if (status != KSUCCESS)
216 (void) sprintf(PQerrormsg,
217 "pg_krb4_sendauth: kerberos error: %s\n",
218 krb_err_txt[status]);
227 /*----------------------------------------------------------------
228 * MIT Kerberos authentication system - protocol version 5
229 *----------------------------------------------------------------
232 #include "krb5/krb5.h"
235 * pg_an_to_ln -- return the local name corresponding to an authentication
238 * XXX Assumes that the first aname component is the user name. This is NOT
239 * necessarily so, since an aname can actually be something out of your
240 * worst X.400 nightmare, like
241 * ORGANIZATION=U. C. Berkeley/NAME=Paul M. Aoki@CS.BERKELEY.EDU
242 * Note that the MIT an_to_ln code does the same thing if you don't
243 * provide an aname mapping database...it may be a better idea to use
244 * krb5_an_to_ln, except that it punts if multiple components are found,
245 * and we can't afford to punt.
248 pg_an_to_ln(const char *aname)
252 if ((p = strchr(aname, '/')) || (p = strchr(aname, '@')))
259 * pg_krb5_init -- initialization performed before any Kerberos calls are made
261 * With v5, we can no longer set the ticket (credential cache) file name;
262 * we now have to provide a file handle for the open (well, "resolved")
263 * ticket file everywhere.
270 krb5_error_code code;
273 char tktbuf[MAXPATHLEN];
274 static krb5_ccache ccache = (krb5_ccache) NULL;
280 * If the user set PGREALM, then we use a ticket file with a special
281 * name: <usual-ticket-file-name>@<PGREALM-value>
283 if (!(defname = krb5_cc_default_name()))
285 (void) sprintf(PQerrormsg,
286 "pg_krb5_init: krb5_cc_default_name failed\n");
287 return (krb5_ccache) NULL;
289 strcpy(tktbuf, defname);
290 if (realm = getenv("PGREALM"))
293 strcat(tktbuf, realm);
296 if (code = krb5_cc_resolve(tktbuf, &ccache))
298 (void) sprintf(PQerrormsg,
299 "pg_krb5_init: Kerberos error %d in krb5_cc_resolve\n", code);
300 com_err("pg_krb5_init", code, "in krb5_cc_resolve");
301 return (krb5_ccache) NULL;
307 * pg_krb5_authname -- returns a pointer to static space containing whatever
308 * name the user has authenticated to the system
310 * We obtain this information by digging around in the ticket file.
313 pg_krb5_authname(const char *PQerrormsg)
316 krb5_principal principal;
317 krb5_error_code code;
318 static char *authname = (char *) NULL;
323 ccache = pg_krb5_init(); /* don't free this */
325 if (code = krb5_cc_get_principal(ccache, &principal))
327 (void) sprintf(PQerrormsg,
328 "pg_krb5_authname: Kerberos error %d in krb5_cc_get_principal\n", code);
329 com_err("pg_krb5_authname", code, "in krb5_cc_get_principal");
330 return (char *) NULL;
332 if (code = krb5_unparse_name(principal, &authname))
334 (void) sprintf(PQerrormsg,
335 "pg_krb5_authname: Kerberos error %d in krb5_unparse_name\n", code);
336 com_err("pg_krb5_authname", code, "in krb5_unparse_name");
337 krb5_free_principal(principal);
338 return (char *) NULL;
340 krb5_free_principal(principal);
341 return pg_an_to_ln(authname);
345 * pg_krb5_sendauth -- client routine to send authentication information to
348 * This routine does not do mutual authentication, nor does it return enough
349 * information to do encrypted connections. But then, if we want to do
350 * encrypted connections, we'll have to redesign the whole RPC mechanism
353 * Server hostnames are canonicalized v4-style, i.e., all domain suffixes
354 * are simply chopped off. Hence, we are assuming that you've entered your
355 * server instances as
356 * <value-of-PG_KRB_SRVNAM>/<canonicalized-hostname>
357 * in the PGREALM (or local) database. This is probably a bad assumption.
360 pg_krb5_sendauth(const char *PQerrormsg, int sock,
361 struct sockaddr_in * laddr,
362 struct sockaddr_in * raddr,
363 const char *hostname)
365 char servbuf[MAXHOSTNAMELEN + 1 +
366 sizeof(PG_KRB_SRVNAM)];
369 krb5_error_code code;
370 krb5_principal client,
373 krb5_error *error = (krb5_error *) NULL;
375 ccache = pg_krb5_init(); /* don't free this */
378 * set up client -- this is easy, we can get it out of the ticket
381 if (code = krb5_cc_get_principal(ccache, &client))
383 (void) sprintf(PQerrormsg,
384 "pg_krb5_sendauth: Kerberos error %d in krb5_cc_get_principal\n", code);
385 com_err("pg_krb5_sendauth", code, "in krb5_cc_get_principal");
390 * set up server -- canonicalize as described above
392 strcpy(servbuf, PG_KRB_SRVNAM);
393 *(hostp = servbuf + (sizeof(PG_KRB_SRVNAM) - 1)) = '/';
394 if (hostname || *hostname)
395 strncpy(++hostp, hostname, MAXHOSTNAMELEN);
398 if (gethostname(++hostp, MAXHOSTNAMELEN) < 0)
399 strcpy(hostp, "localhost");
401 if (hostp = strchr(hostp, '.'))
403 if (realm = getenv("PGREALM"))
405 strcat(servbuf, "@");
406 strcat(servbuf, realm);
408 if (code = krb5_parse_name(servbuf, &server))
410 (void) sprintf(PQerrormsg,
411 "pg_krb5_sendauth: Kerberos error %d in krb5_parse_name\n", code);
412 com_err("pg_krb5_sendauth", code, "in krb5_parse_name");
413 krb5_free_principal(client);
418 * The only thing we want back from krb5_sendauth is an error status
419 * and any error messages.
421 if (code = krb5_sendauth((krb5_pointer) & sock,
426 (krb5_checksum *) NULL,
430 (krb5_keyblock **) NULL,
432 (krb5_ap_rep_enc_part **) NULL))
434 if ((code == KRB5_SENDAUTH_REJECTED) && error)
436 (void) sprintf(PQerrormsg,
437 "pg_krb5_sendauth: authentication rejected: \"%*s\"\n",
438 error->text.length, error->text.data);
442 (void) sprintf(PQerrormsg,
443 "pg_krb5_sendauth: Kerberos error %d in krb5_sendauth\n", code);
444 com_err("pg_krb5_sendauth", code, "in krb5_sendauth");
447 krb5_free_principal(client);
448 krb5_free_principal(server);
449 return code ? STATUS_ERROR : STATUS_OK;
455 pg_password_sendauth(PGconn *conn, const char *password, AuthRequest areq)
457 /* Encrypt the password if needed. */
459 if (areq == AUTH_REQ_CRYPT)
460 password = crypt(password, conn->salt);
462 return pqPacketSend(conn, password, strlen(password) + 1);
466 * fe_sendauth -- client demux routine for outgoing authentication information
469 fe_sendauth(AuthRequest areq, PGconn *conn, const char *hostname,
470 const char *password, char *PQerrormsg)
479 if (pg_krb4_sendauth(PQerrormsg, conn->sock, &conn->laddr.in,
481 hostname) != STATUS_OK)
483 (void) sprintf(PQerrormsg,
484 "fe_sendauth: krb4 authentication failed\n");
489 (void) sprintf(PQerrormsg,
490 "fe_sendauth: krb4 authentication not supported\n");
496 if (pg_krb5_sendauth(PQerrormsg, conn->sock, &conn->laddr.in,
498 hostname) != STATUS_OK)
500 (void) sprintf(PQerrormsg,
501 "fe_sendauth: krb5 authentication failed\n");
506 (void) sprintf(PQerrormsg,
507 "fe_sendauth: krb5 authentication not supported\n");
511 case AUTH_REQ_PASSWORD:
513 if (password == NULL || *password == '\0')
515 (void) sprintf(PQerrormsg,
516 "fe_sendauth: no password supplied\n");
519 if (pg_password_sendauth(conn, password, areq) != STATUS_OK)
521 (void) sprintf(PQerrormsg,
522 "fe_sendauth: error sending password authentication\n");
529 (void) sprintf(PQerrormsg,
530 "fe_sendauth: authentication type %u not supported\n", areq);
541 * Set/return the authentication service currently selected for use by the
542 * frontend. (You can only use one in the frontend, obviously.)
544 static int pg_authsvc = -1;
547 fe_setauthsvc(const char *name, char *PQerrormsg)
551 for (i = 0; i < n_authsvcs; ++i)
552 if (!strcmp(name, authsvcs[i].name))
559 (void) sprintf(PQerrormsg,
560 "fe_setauthsvc: invalid name: %s, ignoring...\n",
567 fe_getauthsvc(char *PQerrormsg)
569 if (pg_authsvc < 0 || pg_authsvc >= n_authsvcs)
570 fe_setauthsvc(DEFAULT_CLIENT_AUTHSVC, PQerrormsg);
571 return authsvcs[pg_authsvc].msgtype;
575 * fe_getauthname -- returns a pointer to dynamic space containing whatever
576 * name the user has authenticated to the system
577 * if there is an error, return the error message in PQerrormsg
580 fe_getauthname(char *PQerrormsg)
582 char *name = (char *) NULL;
583 char *authn = (char *) NULL;
586 authsvc = fe_getauthsvc(PQerrormsg);
587 switch ((int) authsvc)
590 case STARTUP_KRB4_MSG:
591 name = pg_krb4_authname(PQerrormsg);
595 case STARTUP_KRB5_MSG:
596 name = pg_krb5_authname(PQerrormsg);
603 DWORD namesize = sizeof(username) - 1;
605 if (GetUserName(username, &namesize))
608 struct passwd *pw = getpwuid(geteuid());
616 (void) sprintf(PQerrormsg,
617 "fe_getauthname: invalid authentication system: %d\n",
622 if (name && (authn = (char *) malloc(strlen(name) + 1)))