fe-auth.c
上传用户:blenddy
上传日期:2007-01-07
资源大小:6495k
文件大小:16k
源码类别:

数据库系统

开发平台:

Unix_Linux

  1. /*-------------------------------------------------------------------------
  2.  *
  3.  * fe-auth.c
  4.  *    The front-end (client) authorization routines
  5.  *
  6.  * Copyright (c) 1994, Regents of the University of California
  7.  *
  8.  *
  9.  * IDENTIFICATION
  10.  *   $Header: /usr/local/cvsroot/pgsql/src/interfaces/libpq/fe-auth.c,v 1.29 1999/05/25 16:15:10 momjian Exp $
  11.  *
  12.  *-------------------------------------------------------------------------
  13.  */
  14. /*
  15.  * INTERFACE ROUTINES
  16.  *    frontend (client) routines:
  17.  * fe_sendauth send authentication information
  18.  * fe_getauthname get user's name according to the client side
  19.  * of the authentication system
  20.  * fe_setauthsvc set frontend authentication service
  21.  * fe_getauthsvc get current frontend authentication service
  22.  *
  23.  *
  24.  *
  25.  */
  26. #include "libpq-fe.h"
  27. #include "libpq-int.h"
  28. #include "fe-auth.h"
  29. #include "postgres.h"
  30. #ifdef WIN32
  31. #include "win32.h"
  32. #else
  33. #include <string.h>
  34. #include <sys/param.h> /* for MAXHOSTNAMELEN on most */
  35. #ifndef  MAXHOSTNAMELEN
  36. #include <netdb.h> /* for MAXHOSTNAMELEN on some */
  37. #endif
  38. #if !defined(NO_UNISTD_H)
  39. #include <unistd.h>
  40. #endif
  41. #include <pwd.h>
  42. #endif  /* WIN32 */
  43. #ifdef HAVE_CRYPT_H
  44. #include <crypt.h>
  45. #endif
  46. /*----------------------------------------------------------------
  47.  * common definitions for generic fe/be routines
  48.  *----------------------------------------------------------------
  49.  */
  50. struct authsvc
  51. {
  52. char name[NAMEDATALEN]; /* service nickname (for command
  53.  * line) */
  54. MsgType msgtype; /* startup packet header type */
  55. int allowed; /* initially allowed (before command line
  56.  * option parsing)? */
  57. };
  58. /*
  59.  * Command-line parsing routines use this structure to map nicknames
  60.  * onto service types (and the startup packets to use with them).
  61.  *
  62.  * Programs receiving an authentication request use this structure to
  63.  * decide which authentication service types are currently permitted.
  64.  * By default, all authentication systems compiled into the system are
  65.  * allowed.  Unauthenticated connections are disallowed unless there
  66.  * isn't any authentication system.
  67.  */
  68. static struct authsvc authsvcs[] = {
  69. #ifdef KRB4
  70. {"krb4", STARTUP_KRB4_MSG, 1},
  71. {"kerberos", STARTUP_KRB4_MSG, 1},
  72. #endif  /* KRB4 */
  73. #ifdef KRB5
  74. {"krb5", STARTUP_KRB5_MSG, 1},
  75. {"kerberos", STARTUP_KRB5_MSG, 1},
  76. #endif  /* KRB5 */
  77. {UNAUTHNAME, STARTUP_MSG,
  78. #if defined(KRB4) || defined(KRB5)
  79. 0
  80. #else /* !(KRB4 || KRB5) */
  81. 1
  82. #endif  /* !(KRB4 || KRB5) */
  83. },
  84. {"password", STARTUP_PASSWORD_MSG, 0}
  85. };
  86. static int n_authsvcs = sizeof(authsvcs) / sizeof(struct authsvc);
  87. #ifdef KRB4
  88. /*----------------------------------------------------------------
  89.  * MIT Kerberos authentication system - protocol version 4
  90.  *----------------------------------------------------------------
  91.  */
  92. #include "krb.h"
  93. /* for some reason, this is not defined in krb.h ... */
  94. extern char *tkt_string(void);
  95. /*
  96.  * pg_krb4_init -- initialization performed before any Kerberos calls are made
  97.  *
  98.  * For v4, all we need to do is make sure the library routines get the right
  99.  * ticket file if we want them to see a special one.  (They will open the file
  100.  * themselves.)
  101.  */
  102. static void
  103. pg_krb4_init()
  104. {
  105. char    *realm;
  106. static init_done = 0;
  107. if (init_done)
  108. return;
  109. init_done = 1;
  110. /*
  111.  * If the user set PGREALM, then we use a ticket file with a special
  112.  * name: <usual-ticket-file-name>@<PGREALM-value>
  113.  */
  114. if (realm = getenv("PGREALM"))
  115. {
  116. char tktbuf[MAXPATHLEN];
  117. (void) sprintf(tktbuf, "%s@%s", tkt_string(), realm);
  118. krb_set_tkt_string(tktbuf);
  119. }
  120. }
  121. /*
  122.  * pg_krb4_authname -- returns a pointer to static space containing whatever
  123.  *    name the user has authenticated to the system
  124.  *
  125.  * We obtain this information by digging around in the ticket file.
  126.  */
  127. static char *
  128. pg_krb4_authname(char *PQerrormsg)
  129. {
  130. char instance[INST_SZ + 1];
  131. char realm[REALM_SZ + 1];
  132. int status;
  133. static char name[SNAME_SZ + 1] = "";
  134. if (name[0])
  135. return name;
  136. pg_krb4_init();
  137. name[SNAME_SZ] = '';
  138. status = krb_get_tf_fullname(tkt_string(), name, instance, realm);
  139. if (status != KSUCCESS)
  140. {
  141. (void) sprintf(PQerrormsg,
  142.    "pg_krb4_authname: krb_get_tf_fullname: %sn",
  143.    krb_err_txt[status]);
  144. return (char *) NULL;
  145. }
  146. return name;
  147. }
  148. /*
  149.  * pg_krb4_sendauth -- client routine to send authentication information to
  150.  *    the server
  151.  *
  152.  * This routine does not do mutual authentication, nor does it return enough
  153.  * information to do encrypted connections.  But then, if we want to do
  154.  * encrypted connections, we'll have to redesign the whole RPC mechanism
  155.  * anyway.
  156.  *
  157.  * If the user is too lazy to feed us a hostname, we try to come up with
  158.  * something other than "localhost" since the hostname is used as an
  159.  * instance and instance names in v4 databases are usually actual hostnames
  160.  * (canonicalized to omit all domain suffixes).
  161.  */
  162. static int
  163. pg_krb4_sendauth(const char *PQerrormsg, int sock,
  164.  struct sockaddr_in * laddr,
  165.  struct sockaddr_in * raddr,
  166.  const char *hostname)
  167. {
  168. long krbopts = 0; /* one-way authentication */
  169. KTEXT_ST clttkt;
  170. int status;
  171. char hostbuf[MAXHOSTNAMELEN];
  172. const char *realm = getenv("PGREALM"); /* NULL == current realm */
  173. if (!hostname || !(*hostname))
  174. {
  175. if (gethostname(hostbuf, MAXHOSTNAMELEN) < 0)
  176. strcpy(hostbuf, "localhost");
  177. hostname = hostbuf;
  178. }
  179. pg_krb4_init();
  180. status = krb_sendauth(krbopts,
  181.   sock,
  182.   &clttkt,
  183.   PG_KRB_SRVNAM,
  184.   hostname,
  185.   realm,
  186.   (u_long) 0,
  187.   (MSG_DAT *) NULL,
  188.   (CREDENTIALS *) NULL,
  189.   (Key_schedule *) NULL,
  190.   laddr,
  191.   raddr,
  192.   PG_KRB4_VERSION);
  193. if (status != KSUCCESS)
  194. {
  195. (void) sprintf(PQerrormsg,
  196.    "pg_krb4_sendauth: kerberos error: %sn",
  197.    krb_err_txt[status]);
  198. return STATUS_ERROR;
  199. }
  200. return STATUS_OK;
  201. }
  202. #endif  /* KRB4 */
  203. #ifdef KRB5
  204. /*----------------------------------------------------------------
  205.  * MIT Kerberos authentication system - protocol version 5
  206.  *----------------------------------------------------------------
  207.  */
  208. #include "krb5/krb5.h"
  209. /*
  210.  * pg_an_to_ln -- return the local name corresponding to an authentication
  211.  *   name
  212.  *
  213.  * XXX Assumes that the first aname component is the user name.  This is NOT
  214.  *    necessarily so, since an aname can actually be something out of your
  215.  *    worst X.400 nightmare, like
  216.  *   ORGANIZATION=U. C. Berkeley/NAME=Paul M. Aoki@CS.BERKELEY.EDU
  217.  *    Note that the MIT an_to_ln code does the same thing if you don't
  218.  *    provide an aname mapping database...it may be a better idea to use
  219.  *    krb5_an_to_ln, except that it punts if multiple components are found,
  220.  *    and we can't afford to punt.
  221.  */
  222. static char *
  223. pg_an_to_ln(const char *aname)
  224. {
  225. char    *p;
  226. if ((p = strchr(aname, '/')) || (p = strchr(aname, '@')))
  227. *p = '';
  228. return aname;
  229. }
  230. /*
  231.  * pg_krb5_init -- initialization performed before any Kerberos calls are made
  232.  *
  233.  * With v5, we can no longer set the ticket (credential cache) file name;
  234.  * we now have to provide a file handle for the open (well, "resolved")
  235.  * ticket file everywhere.
  236.  *
  237.  */
  238. static int
  239. krb5_ccache
  240. pg_krb5_init(void)
  241. {
  242. krb5_error_code code;
  243. char    *realm,
  244.    *defname;
  245. char tktbuf[MAXPATHLEN];
  246. static krb5_ccache ccache = (krb5_ccache) NULL;
  247. if (ccache)
  248. return ccache;
  249. /*
  250.  * If the user set PGREALM, then we use a ticket file with a special
  251.  * name: <usual-ticket-file-name>@<PGREALM-value>
  252.  */
  253. if (!(defname = krb5_cc_default_name()))
  254. {
  255. (void) sprintf(PQerrormsg,
  256.    "pg_krb5_init: krb5_cc_default_name failedn");
  257. return (krb5_ccache) NULL;
  258. }
  259. strcpy(tktbuf, defname);
  260. if (realm = getenv("PGREALM"))
  261. {
  262. strcat(tktbuf, "@");
  263. strcat(tktbuf, realm);
  264. }
  265. if (code = krb5_cc_resolve(tktbuf, &ccache))
  266. {
  267. (void) sprintf(PQerrormsg,
  268.    "pg_krb5_init: Kerberos error %d in krb5_cc_resolven", code);
  269. com_err("pg_krb5_init", code, "in krb5_cc_resolve");
  270. return (krb5_ccache) NULL;
  271. }
  272. return ccache;
  273. }
  274. /*
  275.  * pg_krb5_authname -- returns a pointer to static space containing whatever
  276.  *    name the user has authenticated to the system
  277.  *
  278.  * We obtain this information by digging around in the ticket file.
  279.  */
  280. static const char *
  281. pg_krb5_authname(const char *PQerrormsg)
  282. {
  283. krb5_ccache ccache;
  284. krb5_principal principal;
  285. krb5_error_code code;
  286. static char *authname = (char *) NULL;
  287. if (authname)
  288. return authname;
  289. ccache = pg_krb5_init(); /* don't free this */
  290. if (code = krb5_cc_get_principal(ccache, &principal))
  291. {
  292. (void) sprintf(PQerrormsg,
  293.    "pg_krb5_authname: Kerberos error %d in krb5_cc_get_principaln", code);
  294. com_err("pg_krb5_authname", code, "in krb5_cc_get_principal");
  295. return (char *) NULL;
  296. }
  297. if (code = krb5_unparse_name(principal, &authname))
  298. {
  299. (void) sprintf(PQerrormsg,
  300.    "pg_krb5_authname: Kerberos error %d in krb5_unparse_namen", code);
  301. com_err("pg_krb5_authname", code, "in krb5_unparse_name");
  302. krb5_free_principal(principal);
  303. return (char *) NULL;
  304. }
  305. krb5_free_principal(principal);
  306. return pg_an_to_ln(authname);
  307. }
  308. /*
  309.  * pg_krb5_sendauth -- client routine to send authentication information to
  310.  *    the server
  311.  *
  312.  * This routine does not do mutual authentication, nor does it return enough
  313.  * information to do encrypted connections.  But then, if we want to do
  314.  * encrypted connections, we'll have to redesign the whole RPC mechanism
  315.  * anyway.
  316.  *
  317.  * Server hostnames are canonicalized v4-style, i.e., all domain suffixes
  318.  * are simply chopped off. Hence, we are assuming that you've entered your
  319.  * server instances as
  320.  * <value-of-PG_KRB_SRVNAM>/<canonicalized-hostname>
  321.  * in the PGREALM (or local) database. This is probably a bad assumption.
  322.  */
  323. static int
  324. pg_krb5_sendauth(const char *PQerrormsg, int sock,
  325.  struct sockaddr_in * laddr,
  326.  struct sockaddr_in * raddr,
  327.  const char *hostname)
  328. {
  329. char servbuf[MAXHOSTNAMELEN + 1 +
  330. sizeof(PG_KRB_SRVNAM)];
  331. const char *hostp;
  332. const char *realm;
  333. krb5_error_code code;
  334. krb5_principal client,
  335. server;
  336. krb5_ccache ccache;
  337. krb5_error *error = (krb5_error *) NULL;
  338. ccache = pg_krb5_init(); /* don't free this */
  339. /*
  340.  * set up client -- this is easy, we can get it out of the ticket
  341.  * file.
  342.  */
  343. if (code = krb5_cc_get_principal(ccache, &client))
  344. {
  345. (void) sprintf(PQerrormsg,
  346.    "pg_krb5_sendauth: Kerberos error %d in krb5_cc_get_principaln", code);
  347. com_err("pg_krb5_sendauth", code, "in krb5_cc_get_principal");
  348. return STATUS_ERROR;
  349. }
  350. /*
  351.  * set up server -- canonicalize as described above
  352.  */
  353. strcpy(servbuf, PG_KRB_SRVNAM);
  354. *(hostp = servbuf + (sizeof(PG_KRB_SRVNAM) - 1)) = '/';
  355. if (hostname || *hostname)
  356. strncpy(++hostp, hostname, MAXHOSTNAMELEN);
  357. else
  358. {
  359. if (gethostname(++hostp, MAXHOSTNAMELEN) < 0)
  360. strcpy(hostp, "localhost");
  361. }
  362. if (hostp = strchr(hostp, '.'))
  363. *hostp = '';
  364. if (realm = getenv("PGREALM"))
  365. {
  366. strcat(servbuf, "@");
  367. strcat(servbuf, realm);
  368. }
  369. if (code = krb5_parse_name(servbuf, &server))
  370. {
  371. (void) sprintf(PQerrormsg,
  372. "pg_krb5_sendauth: Kerberos error %d in krb5_parse_namen", code);
  373. com_err("pg_krb5_sendauth", code, "in krb5_parse_name");
  374. krb5_free_principal(client);
  375. return STATUS_ERROR;
  376. }
  377. /*
  378.  * The only thing we want back from krb5_sendauth is an error status
  379.  * and any error messages.
  380.  */
  381. if (code = krb5_sendauth((krb5_pointer) & sock,
  382.  PG_KRB5_VERSION,
  383.  client,
  384.  server,
  385.  (krb5_flags) 0,
  386.  (krb5_checksum *) NULL,
  387.  (krb5_creds *) NULL,
  388.  ccache,
  389.  (krb5_int32 *) NULL,
  390.  (krb5_keyblock **) NULL,
  391.  &error,
  392.  (krb5_ap_rep_enc_part **) NULL))
  393. {
  394. if ((code == KRB5_SENDAUTH_REJECTED) && error)
  395. {
  396. (void) sprintf(PQerrormsg,
  397.   "pg_krb5_sendauth: authentication rejected: "%*s"n",
  398.    error->text.length, error->text.data);
  399. }
  400. else
  401. {
  402. (void) sprintf(PQerrormsg,
  403.    "pg_krb5_sendauth: Kerberos error %d in krb5_sendauthn", code);
  404. com_err("pg_krb5_sendauth", code, "in krb5_sendauth");
  405. }
  406. }
  407. krb5_free_principal(client);
  408. krb5_free_principal(server);
  409. return code ? STATUS_ERROR : STATUS_OK;
  410. }
  411. #endif  /* KRB5 */
  412. static int
  413. pg_password_sendauth(PGconn *conn, const char *password, AuthRequest areq)
  414. {
  415. /* Encrypt the password if needed. */
  416. if (areq == AUTH_REQ_CRYPT)
  417. password = crypt(password, conn->salt);
  418. return pqPacketSend(conn, password, strlen(password) + 1);
  419. }
  420. /*
  421.  * fe_sendauth -- client demux routine for outgoing authentication information
  422.  */
  423. int
  424. fe_sendauth(AuthRequest areq, PGconn *conn, const char *hostname,
  425. const char *password, char *PQerrormsg)
  426. {
  427. switch (areq)
  428. {
  429. case AUTH_REQ_OK:
  430. break;
  431. case AUTH_REQ_KRB4:
  432. #ifdef KRB4
  433. if (pg_krb4_sendauth(PQerrormsg, conn->sock, &conn->laddr.in,
  434.  &conn->raddr.in,
  435.  hostname) != STATUS_OK)
  436. {
  437. (void) sprintf(PQerrormsg,
  438. "fe_sendauth: krb4 authentication failedn");
  439. return STATUS_ERROR;
  440. }
  441. break;
  442. #else
  443. (void) sprintf(PQerrormsg,
  444.  "fe_sendauth: krb4 authentication not supportedn");
  445. return STATUS_ERROR;
  446. #endif
  447. case AUTH_REQ_KRB5:
  448. #ifdef KRB5
  449. if (pg_krb5_sendauth(PQerrormsg, conn->sock, &conn->laddr.in,
  450.  &conn->raddr.in,
  451.  hostname) != STATUS_OK)
  452. {
  453. (void) sprintf(PQerrormsg,
  454. "fe_sendauth: krb5 authentication failedn");
  455. return STATUS_ERROR;
  456. }
  457. break;
  458. #else
  459. (void) sprintf(PQerrormsg,
  460.  "fe_sendauth: krb5 authentication not supportedn");
  461. return STATUS_ERROR;
  462. #endif
  463. case AUTH_REQ_PASSWORD:
  464. case AUTH_REQ_CRYPT:
  465. if (password == NULL || *password == '')
  466. {
  467. (void) sprintf(PQerrormsg,
  468.    "fe_sendauth: no password suppliedn");
  469. return STATUS_ERROR;
  470. }
  471. if (pg_password_sendauth(conn, password, areq) != STATUS_OK)
  472. {
  473. (void) sprintf(PQerrormsg,
  474.  "fe_sendauth: error sending password authenticationn");
  475. return STATUS_ERROR;
  476. }
  477. break;
  478. default:
  479. (void) sprintf(PQerrormsg,
  480. "fe_sendauth: authentication type %u not supportedn", areq);
  481. return STATUS_ERROR;
  482. }
  483. return STATUS_OK;
  484. }
  485. /*
  486.  * fe_setauthsvc
  487.  * fe_getauthsvc
  488.  *
  489.  * Set/return the authentication service currently selected for use by the
  490.  * frontend. (You can only use one in the frontend, obviously.)
  491.  */
  492. static int pg_authsvc = -1;
  493. void
  494. fe_setauthsvc(const char *name, char *PQerrormsg)
  495. {
  496. int i;
  497. for (i = 0; i < n_authsvcs; ++i)
  498. if (!strcmp(name, authsvcs[i].name))
  499. {
  500. pg_authsvc = i;
  501. break;
  502. }
  503. if (i == n_authsvcs)
  504. {
  505. (void) sprintf(PQerrormsg,
  506.    "fe_setauthsvc: invalid name: %s, ignoring...n",
  507.    name);
  508. }
  509. return;
  510. }
  511. MsgType
  512. fe_getauthsvc(char *PQerrormsg)
  513. {
  514. if (pg_authsvc < 0 || pg_authsvc >= n_authsvcs)
  515. fe_setauthsvc(DEFAULT_CLIENT_AUTHSVC, PQerrormsg);
  516. return authsvcs[pg_authsvc].msgtype;
  517. }
  518. /*
  519.  * fe_getauthname -- returns a pointer to dynamic space containing whatever
  520.  *  name the user has authenticated to the system
  521.  * if there is an error, return the error message in PQerrormsg
  522.  */
  523. char *
  524. fe_getauthname(char *PQerrormsg)
  525. {
  526. char    *name = (char *) NULL;
  527. char    *authn = (char *) NULL;
  528. MsgType authsvc;
  529. authsvc = fe_getauthsvc(PQerrormsg);
  530. switch ((int) authsvc)
  531. {
  532. #ifdef KRB4
  533. case STARTUP_KRB4_MSG:
  534. name = pg_krb4_authname(PQerrormsg);
  535. break;
  536. #endif
  537. #ifdef KRB5
  538. case STARTUP_KRB5_MSG:
  539. name = pg_krb5_authname(PQerrormsg);
  540. break;
  541. #endif
  542. case STARTUP_MSG:
  543. {
  544. #ifdef WIN32
  545. char username[128];
  546. DWORD namesize = sizeof(username) - 1;
  547. if (GetUserName(username, &namesize))
  548. name = username;
  549. #else
  550. struct passwd *pw = getpwuid(geteuid());
  551. if (pw)
  552. name = pw->pw_name;
  553. #endif
  554. }
  555. break;
  556. default:
  557. (void) sprintf(PQerrormsg,
  558.    "fe_getauthname: invalid authentication system: %dn",
  559.    authsvc);
  560. break;
  561. }
  562. if (name && (authn = (char *) malloc(strlen(name) + 1)))
  563. strcpy(authn, name);
  564. return authn;
  565. }