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.29 1999/05/25 16:15:10 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
29 #include "libpq-int.h"
37 #include <sys/param.h> /* for MAXHOSTNAMELEN on most */
38 #ifndef MAXHOSTNAMELEN
39 #include <netdb.h> /* for MAXHOSTNAMELEN on some */
41 #if !defined(NO_UNISTD_H)
52 /*----------------------------------------------------------------
53 * common definitions for generic fe/be routines
54 *----------------------------------------------------------------
59 char name[NAMEDATALEN]; /* service nickname (for command
61 MsgType msgtype; /* startup packet header type */
62 int allowed; /* initially allowed (before command line
67 * Command-line parsing routines use this structure to map nicknames
68 * onto service types (and the startup packets to use with them).
70 * Programs receiving an authentication request use this structure to
71 * decide which authentication service types are currently permitted.
72 * By default, all authentication systems compiled into the system are
73 * allowed. Unauthenticated connections are disallowed unless there
74 * isn't any authentication system.
76 static struct authsvc authsvcs[] = {
78 {"krb4", STARTUP_KRB4_MSG, 1},
79 {"kerberos", STARTUP_KRB4_MSG, 1},
82 {"krb5", STARTUP_KRB5_MSG, 1},
83 {"kerberos", STARTUP_KRB5_MSG, 1},
85 {UNAUTHNAME, STARTUP_MSG,
86 #if defined(KRB4) || defined(KRB5)
88 #else /* !(KRB4 || KRB5) */
90 #endif /* !(KRB4 || KRB5) */
92 {"password", STARTUP_PASSWORD_MSG, 0}
95 static int n_authsvcs = sizeof(authsvcs) / sizeof(struct authsvc);
98 /*----------------------------------------------------------------
99 * MIT Kerberos authentication system - protocol version 4
100 *----------------------------------------------------------------
105 /* for some reason, this is not defined in krb.h ... */
106 extern char *tkt_string(void);
109 * pg_krb4_init -- initialization performed before any Kerberos calls are made
111 * For v4, all we need to do is make sure the library routines get the right
112 * ticket file if we want them to see a special one. (They will open the file
119 static init_done = 0;
126 * If the user set PGREALM, then we use a ticket file with a special
127 * name: <usual-ticket-file-name>@<PGREALM-value>
129 if (realm = getenv("PGREALM"))
131 char tktbuf[MAXPATHLEN];
133 (void) sprintf(tktbuf, "%s@%s", tkt_string(), realm);
134 krb_set_tkt_string(tktbuf);
139 * pg_krb4_authname -- returns a pointer to static space containing whatever
140 * name the user has authenticated to the system
142 * We obtain this information by digging around in the ticket file.
145 pg_krb4_authname(char *PQerrormsg)
147 char instance[INST_SZ + 1];
148 char realm[REALM_SZ + 1];
150 static char name[SNAME_SZ + 1] = "";
157 name[SNAME_SZ] = '\0';
158 status = krb_get_tf_fullname(tkt_string(), name, instance, realm);
159 if (status != KSUCCESS)
161 (void) sprintf(PQerrormsg,
162 "pg_krb4_authname: krb_get_tf_fullname: %s\n",
163 krb_err_txt[status]);
164 return (char *) NULL;
170 * pg_krb4_sendauth -- client routine to send authentication information to
173 * This routine does not do mutual authentication, nor does it return enough
174 * information to do encrypted connections. But then, if we want to do
175 * encrypted connections, we'll have to redesign the whole RPC mechanism
178 * If the user is too lazy to feed us a hostname, we try to come up with
179 * something other than "localhost" since the hostname is used as an
180 * instance and instance names in v4 databases are usually actual hostnames
181 * (canonicalized to omit all domain suffixes).
184 pg_krb4_sendauth(const char *PQerrormsg, int sock,
185 struct sockaddr_in * laddr,
186 struct sockaddr_in * raddr,
187 const char *hostname)
189 long krbopts = 0; /* one-way authentication */
192 char hostbuf[MAXHOSTNAMELEN];
193 const char *realm = getenv("PGREALM"); /* NULL == current realm */
195 if (!hostname || !(*hostname))
197 if (gethostname(hostbuf, MAXHOSTNAMELEN) < 0)
198 strcpy(hostbuf, "localhost");
204 status = krb_sendauth(krbopts,
212 (CREDENTIALS *) NULL,
213 (Key_schedule *) NULL,
217 if (status != KSUCCESS)
219 (void) sprintf(PQerrormsg,
220 "pg_krb4_sendauth: kerberos error: %s\n",
221 krb_err_txt[status]);
230 /*----------------------------------------------------------------
231 * MIT Kerberos authentication system - protocol version 5
232 *----------------------------------------------------------------
235 #include "krb5/krb5.h"
238 * pg_an_to_ln -- return the local name corresponding to an authentication
241 * XXX Assumes that the first aname component is the user name. This is NOT
242 * necessarily so, since an aname can actually be something out of your
243 * worst X.400 nightmare, like
244 * ORGANIZATION=U. C. Berkeley/NAME=Paul M. Aoki@CS.BERKELEY.EDU
245 * Note that the MIT an_to_ln code does the same thing if you don't
246 * provide an aname mapping database...it may be a better idea to use
247 * krb5_an_to_ln, except that it punts if multiple components are found,
248 * and we can't afford to punt.
251 pg_an_to_ln(const char *aname)
255 if ((p = strchr(aname, '/')) || (p = strchr(aname, '@')))
262 * pg_krb5_init -- initialization performed before any Kerberos calls are made
264 * With v5, we can no longer set the ticket (credential cache) file name;
265 * we now have to provide a file handle for the open (well, "resolved")
266 * ticket file everywhere.
273 krb5_error_code code;
276 char tktbuf[MAXPATHLEN];
277 static krb5_ccache ccache = (krb5_ccache) NULL;
283 * If the user set PGREALM, then we use a ticket file with a special
284 * name: <usual-ticket-file-name>@<PGREALM-value>
286 if (!(defname = krb5_cc_default_name()))
288 (void) sprintf(PQerrormsg,
289 "pg_krb5_init: krb5_cc_default_name failed\n");
290 return (krb5_ccache) NULL;
292 strcpy(tktbuf, defname);
293 if (realm = getenv("PGREALM"))
296 strcat(tktbuf, realm);
299 if (code = krb5_cc_resolve(tktbuf, &ccache))
301 (void) sprintf(PQerrormsg,
302 "pg_krb5_init: Kerberos error %d in krb5_cc_resolve\n", code);
303 com_err("pg_krb5_init", code, "in krb5_cc_resolve");
304 return (krb5_ccache) NULL;
310 * pg_krb5_authname -- returns a pointer to static space containing whatever
311 * name the user has authenticated to the system
313 * We obtain this information by digging around in the ticket file.
316 pg_krb5_authname(const char *PQerrormsg)
319 krb5_principal principal;
320 krb5_error_code code;
321 static char *authname = (char *) NULL;
326 ccache = pg_krb5_init(); /* don't free this */
328 if (code = krb5_cc_get_principal(ccache, &principal))
330 (void) sprintf(PQerrormsg,
331 "pg_krb5_authname: Kerberos error %d in krb5_cc_get_principal\n", code);
332 com_err("pg_krb5_authname", code, "in krb5_cc_get_principal");
333 return (char *) NULL;
335 if (code = krb5_unparse_name(principal, &authname))
337 (void) sprintf(PQerrormsg,
338 "pg_krb5_authname: Kerberos error %d in krb5_unparse_name\n", code);
339 com_err("pg_krb5_authname", code, "in krb5_unparse_name");
340 krb5_free_principal(principal);
341 return (char *) NULL;
343 krb5_free_principal(principal);
344 return pg_an_to_ln(authname);
348 * pg_krb5_sendauth -- client routine to send authentication information to
351 * This routine does not do mutual authentication, nor does it return enough
352 * information to do encrypted connections. But then, if we want to do
353 * encrypted connections, we'll have to redesign the whole RPC mechanism
356 * Server hostnames are canonicalized v4-style, i.e., all domain suffixes
357 * are simply chopped off. Hence, we are assuming that you've entered your
358 * server instances as
359 * <value-of-PG_KRB_SRVNAM>/<canonicalized-hostname>
360 * in the PGREALM (or local) database. This is probably a bad assumption.
363 pg_krb5_sendauth(const char *PQerrormsg, int sock,
364 struct sockaddr_in * laddr,
365 struct sockaddr_in * raddr,
366 const char *hostname)
368 char servbuf[MAXHOSTNAMELEN + 1 +
369 sizeof(PG_KRB_SRVNAM)];
372 krb5_error_code code;
373 krb5_principal client,
376 krb5_error *error = (krb5_error *) NULL;
378 ccache = pg_krb5_init(); /* don't free this */
381 * set up client -- this is easy, we can get it out of the ticket
384 if (code = krb5_cc_get_principal(ccache, &client))
386 (void) sprintf(PQerrormsg,
387 "pg_krb5_sendauth: Kerberos error %d in krb5_cc_get_principal\n", code);
388 com_err("pg_krb5_sendauth", code, "in krb5_cc_get_principal");
393 * set up server -- canonicalize as described above
395 strcpy(servbuf, PG_KRB_SRVNAM);
396 *(hostp = servbuf + (sizeof(PG_KRB_SRVNAM) - 1)) = '/';
397 if (hostname || *hostname)
398 strncpy(++hostp, hostname, MAXHOSTNAMELEN);
401 if (gethostname(++hostp, MAXHOSTNAMELEN) < 0)
402 strcpy(hostp, "localhost");
404 if (hostp = strchr(hostp, '.'))
406 if (realm = getenv("PGREALM"))
408 strcat(servbuf, "@");
409 strcat(servbuf, realm);
411 if (code = krb5_parse_name(servbuf, &server))
413 (void) sprintf(PQerrormsg,
414 "pg_krb5_sendauth: Kerberos error %d in krb5_parse_name\n", code);
415 com_err("pg_krb5_sendauth", code, "in krb5_parse_name");
416 krb5_free_principal(client);
421 * The only thing we want back from krb5_sendauth is an error status
422 * and any error messages.
424 if (code = krb5_sendauth((krb5_pointer) & sock,
429 (krb5_checksum *) NULL,
433 (krb5_keyblock **) NULL,
435 (krb5_ap_rep_enc_part **) NULL))
437 if ((code == KRB5_SENDAUTH_REJECTED) && error)
439 (void) sprintf(PQerrormsg,
440 "pg_krb5_sendauth: authentication rejected: \"%*s\"\n",
441 error->text.length, error->text.data);
445 (void) sprintf(PQerrormsg,
446 "pg_krb5_sendauth: Kerberos error %d in krb5_sendauth\n", code);
447 com_err("pg_krb5_sendauth", code, "in krb5_sendauth");
450 krb5_free_principal(client);
451 krb5_free_principal(server);
452 return code ? STATUS_ERROR : STATUS_OK;
458 pg_password_sendauth(PGconn *conn, const char *password, AuthRequest areq)
460 /* Encrypt the password if needed. */
462 if (areq == AUTH_REQ_CRYPT)
463 password = crypt(password, conn->salt);
465 return pqPacketSend(conn, password, strlen(password) + 1);
469 * fe_sendauth -- client demux routine for outgoing authentication information
472 fe_sendauth(AuthRequest areq, PGconn *conn, const char *hostname,
473 const char *password, char *PQerrormsg)
482 if (pg_krb4_sendauth(PQerrormsg, conn->sock, &conn->laddr.in,
484 hostname) != STATUS_OK)
486 (void) sprintf(PQerrormsg,
487 "fe_sendauth: krb4 authentication failed\n");
492 (void) sprintf(PQerrormsg,
493 "fe_sendauth: krb4 authentication not supported\n");
499 if (pg_krb5_sendauth(PQerrormsg, conn->sock, &conn->laddr.in,
501 hostname) != STATUS_OK)
503 (void) sprintf(PQerrormsg,
504 "fe_sendauth: krb5 authentication failed\n");
509 (void) sprintf(PQerrormsg,
510 "fe_sendauth: krb5 authentication not supported\n");
514 case AUTH_REQ_PASSWORD:
516 if (password == NULL || *password == '\0')
518 (void) sprintf(PQerrormsg,
519 "fe_sendauth: no password supplied\n");
522 if (pg_password_sendauth(conn, password, areq) != STATUS_OK)
524 (void) sprintf(PQerrormsg,
525 "fe_sendauth: error sending password authentication\n");
532 (void) sprintf(PQerrormsg,
533 "fe_sendauth: authentication type %u not supported\n", areq);
544 * Set/return the authentication service currently selected for use by the
545 * frontend. (You can only use one in the frontend, obviously.)
547 static int pg_authsvc = -1;
550 fe_setauthsvc(const char *name, char *PQerrormsg)
554 for (i = 0; i < n_authsvcs; ++i)
555 if (!strcmp(name, authsvcs[i].name))
562 (void) sprintf(PQerrormsg,
563 "fe_setauthsvc: invalid name: %s, ignoring...\n",
570 fe_getauthsvc(char *PQerrormsg)
572 if (pg_authsvc < 0 || pg_authsvc >= n_authsvcs)
573 fe_setauthsvc(DEFAULT_CLIENT_AUTHSVC, PQerrormsg);
574 return authsvcs[pg_authsvc].msgtype;
578 * fe_getauthname -- returns a pointer to dynamic space containing whatever
579 * name the user has authenticated to the system
580 * if there is an error, return the error message in PQerrormsg
583 fe_getauthname(char *PQerrormsg)
585 char *name = (char *) NULL;
586 char *authn = (char *) NULL;
589 authsvc = fe_getauthsvc(PQerrormsg);
590 switch ((int) authsvc)
593 case STARTUP_KRB4_MSG:
594 name = pg_krb4_authname(PQerrormsg);
598 case STARTUP_KRB5_MSG:
599 name = pg_krb5_authname(PQerrormsg);
606 DWORD namesize = sizeof(username) - 1;
608 if (GetUserName(username, &namesize))
611 struct passwd *pw = getpwuid(geteuid());
619 (void) sprintf(PQerrormsg,
620 "fe_getauthname: invalid authentication system: %d\n",
625 if (name && (authn = (char *) malloc(strlen(name) + 1)))