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.23 1998/09/01 03:28:50 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 line) */
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 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];
147 char realm[REALM_SZ];
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",
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",
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",
341 com_err("pg_krb5_authname", code, "in krb5_unparse_name");
342 krb5_free_principal(principal);
343 return (char *) NULL;
345 krb5_free_principal(principal);
346 return pg_an_to_ln(authname);
350 * pg_krb5_sendauth -- client routine to send authentication information to
353 * This routine does not do mutual authentication, nor does it return enough
354 * information to do encrypted connections. But then, if we want to do
355 * encrypted connections, we'll have to redesign the whole RPC mechanism
358 * Server hostnames are canonicalized v4-style, i.e., all domain suffixes
359 * are simply chopped off. Hence, we are assuming that you've entered your
360 * server instances as
361 * <value-of-PG_KRB_SRVNAM>/<canonicalized-hostname>
362 * in the PGREALM (or local) database. This is probably a bad assumption.
365 pg_krb5_sendauth(const char *PQerrormsg, int sock,
366 struct sockaddr_in * laddr,
367 struct sockaddr_in * raddr,
368 const char *hostname)
370 char servbuf[MAXHOSTNAMELEN + 1 +
371 sizeof(PG_KRB_SRVNAM)];
374 krb5_error_code code;
375 krb5_principal client,
378 krb5_error *error = (krb5_error *) NULL;
380 ccache = pg_krb5_init(); /* don't free this */
383 * set up client -- this is easy, we can get it out of the ticket
386 if (code = krb5_cc_get_principal(ccache, &client))
388 (void) sprintf(PQerrormsg,
389 "pg_krb5_sendauth: Kerberos error %d in krb5_cc_get_principal\n",
391 com_err("pg_krb5_sendauth", code, "in krb5_cc_get_principal");
396 * set up server -- canonicalize as described above
398 strcpy(servbuf, PG_KRB_SRVNAM);
399 *(hostp = servbuf + (sizeof(PG_KRB_SRVNAM) - 1)) = '/';
400 if (hostname || *hostname)
401 strncpy(++hostp, hostname, MAXHOSTNAMELEN);
404 if (gethostname(++hostp, MAXHOSTNAMELEN) < 0)
405 strcpy(hostp, "localhost");
407 if (hostp = strchr(hostp, '.'))
409 if (realm = getenv("PGREALM"))
411 strcat(servbuf, "@");
412 strcat(servbuf, realm);
414 if (code = krb5_parse_name(servbuf, &server))
416 (void) sprintf(PQerrormsg,
417 "pg_krb5_sendauth: Kerberos error %d in krb5_parse_name\n",
419 com_err("pg_krb5_sendauth", code, "in krb5_parse_name");
420 krb5_free_principal(client);
425 * The only thing we want back from krb5_sendauth is an error status
426 * and any error messages.
428 if (code = krb5_sendauth((krb5_pointer) & sock,
433 (krb5_checksum *) NULL,
437 (krb5_keyblock **) NULL,
439 (krb5_ap_rep_enc_part **) NULL))
441 if ((code == KRB5_SENDAUTH_REJECTED) && error)
443 (void) sprintf(PQerrormsg,
444 "pg_krb5_sendauth: authentication rejected: \"%*s\"\n",
445 error->text.length, error->text.data);
449 (void) sprintf(PQerrormsg,
450 "pg_krb5_sendauth: Kerberos error %d in krb5_sendauth\n",
452 com_err("pg_krb5_sendauth", code, "in krb5_sendauth");
455 krb5_free_principal(client);
456 krb5_free_principal(server);
457 return code ? STATUS_ERROR : STATUS_OK;
463 pg_password_sendauth(PGconn *conn, const char *password, AuthRequest areq)
465 /* Encrypt the password if needed. */
467 if (areq == AUTH_REQ_CRYPT)
468 password = crypt(password, conn->salt);
470 return pqPacketSend(conn, password, strlen(password) + 1);
474 * fe_sendauth -- client demux routine for outgoing authentication information
477 fe_sendauth(AuthRequest areq, PGconn *conn, const char *hostname,
478 const char *password, char *PQerrormsg)
487 if (pg_krb4_sendauth(PQerrormsg, conn->sock, &conn->laddr.in,
489 hostname) != STATUS_OK)
491 (void) sprintf(PQerrormsg,
492 "fe_sendauth: krb4 authentication failed\n");
497 (void) sprintf(PQerrormsg,
498 "fe_sendauth: krb4 authentication not supported\n");
504 if (pg_krb5_sendauth(PQerrormsg, conn->sock, &conn->laddr.in,
506 hostname) != STATUS_OK)
508 (void) sprintf(PQerrormsg,
509 "fe_sendauth: krb5 authentication failed\n");
514 (void) sprintf(PQerrormsg,
515 "fe_sendauth: krb5 authentication not supported\n");
519 case AUTH_REQ_PASSWORD:
521 if (password == NULL || *password == '\0')
523 (void) sprintf(PQerrormsg,
524 "fe_sendauth: no password supplied\n");
527 if (pg_password_sendauth(conn, password, areq) != STATUS_OK)
529 (void) sprintf(PQerrormsg,
530 "fe_sendauth: error sending password authentication\n");
537 (void) sprintf(PQerrormsg,
538 "fe_sendauth: authentication type %u not supported\n", areq);
549 * Set/return the authentication service currently selected for use by the
550 * frontend. (You can only use one in the frontend, obviously.)
552 static pg_authsvc = -1;
555 fe_setauthsvc(const char *name, char *PQerrormsg)
559 for (i = 0; i < n_authsvcs; ++i)
560 if (!strcmp(name, authsvcs[i].name))
567 (void) sprintf(PQerrormsg,
568 "fe_setauthsvc: invalid name: %s, ignoring...\n",
575 fe_getauthsvc(char *PQerrormsg)
577 if (pg_authsvc < 0 || pg_authsvc >= n_authsvcs)
578 fe_setauthsvc(DEFAULT_CLIENT_AUTHSVC, PQerrormsg);
579 return authsvcs[pg_authsvc].msgtype;
583 * fe_getauthname -- returns a pointer to dynamic space containing whatever
584 * name the user has authenticated to the system
585 * if there is an error, return the error message in PQerrormsg
588 fe_getauthname(char *PQerrormsg)
590 char *name = (char *) NULL;
591 char *authn = (char *) NULL;
594 authsvc = fe_getauthsvc(PQerrormsg);
595 switch ((int) authsvc)
598 case STARTUP_KRB4_MSG:
599 name = pg_krb4_authname(PQerrormsg);
603 case STARTUP_KRB5_MSG:
604 name = pg_krb5_authname(PQerrormsg);
611 DWORD namesize = sizeof(username) - 1;
613 if (GetUserName(username,&namesize))
616 struct passwd *pw = getpwuid(geteuid());
624 (void) sprintf(PQerrormsg,
625 "fe_getauthname: invalid authentication system: %d\n",
630 if (name && (authn = (char *) malloc(strlen(name) + 1)))