auth.c 19.0 KB
Newer Older
1 2
/*-------------------------------------------------------------------------
 *
3
 * auth.c
4
 *	  Routines to handle network authentication
5 6 7 8 9
 *
 * Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
B
Bruce Momjian 已提交
10
 *	  $Header: /cvsroot/pgsql/src/backend/libpq/auth.c,v 1.36 1999/05/25 16:08:55 momjian Exp $
11 12 13 14 15 16
 *
 *-------------------------------------------------------------------------
 */
/*
 * INTERFACE ROUTINES
 *
17 18
 *	   backend (postmaster) routines:
 *		be_recvauth				receive authentication information
19 20 21
 */
#include <stdio.h>
#include <string.h>
22
#include <sys/param.h>			/* for MAXHOSTNAMELEN on most */
23
#ifndef  MAXHOSTNAMELEN
24
#include <netdb.h>				/* for MAXHOSTNAMELEN on some */
25
#endif
26
#include <pwd.h>
27
#include <ctype.h>				/* isspace() declaration */
28

29
#include <sys/types.h>			/* needed by in.h on Ultrix */
30 31
#include <netinet/in.h>
#include <arpa/inet.h>
M
Marc G. Fournier 已提交
32

M
Marc G. Fournier 已提交
33 34
#include <postgres.h>
#include <miscadmin.h>
M
Marc G. Fournier 已提交
35

M
Marc G. Fournier 已提交
36 37 38
#include <libpq/auth.h>
#include <libpq/libpq.h>
#include <libpq/hba.h>
39
#include <libpq/password.h>
40
#include <libpq/crypt.h>
41 42


M
 
Marc G. Fournier 已提交
43 44 45 46 47 48 49
static void sendAuthRequest(Port *port, AuthRequest areq, PacketDoneProc handler);
static int	handle_done_auth(void *arg, PacketLen len, void *pkt);
static int	handle_krb4_auth(void *arg, PacketLen len, void *pkt);
static int	handle_krb5_auth(void *arg, PacketLen len, void *pkt);
static int	handle_password_auth(void *arg, PacketLen len, void *pkt);
static int	readPasswordPacket(void *arg, PacketLen len, void *pkt);
static int	pg_passwordv0_recvauth(void *arg, PacketLen len, void *pkt);
50 51 52
static int	checkPassword(Port *port, char *user, char *password);
static int	old_be_recvauth(Port *port);
static int	map_old_to_new(Port *port, UserAuth old, int status);
53 54 55


#ifdef KRB4
56 57 58
/* This has to be ifdef'd out because krb.h does exist.  This needs
   to be fixed.
*/
59 60 61 62 63
/*----------------------------------------------------------------
 * MIT Kerberos authentication system - protocol version 4
 *----------------------------------------------------------------
 */

M
Marc G. Fournier 已提交
64
#include <krb.h>
65 66 67

/*
 * pg_krb4_recvauth -- server routine to receive authentication information
68
 *					   from the client
69 70 71 72 73 74 75
 *
 * Nothing unusual here, except that we compare the username obtained from
 * the client's setup packet to the authenticated name.  (We have to retain
 * the name in the setup packet since we have to retain the ability to handle
 * unauthenticated connections.)
 */
static int
B
Bruce Momjian 已提交
76
pg_krb4_recvauth(Port *port)
77
{
B
Bruce Momjian 已提交
78 79 80 81 82 83 84
	long		krbopts = 0;	/* one-way authentication */
	KTEXT_ST	clttkt;
	char		instance[INST_SZ + 1],
				version[KRB_SENDAUTH_VLEN + 1];
	AUTH_DAT	auth_data;
	Key_schedule key_sched;
	int			status;
85 86 87 88

	strcpy(instance, "*");		/* don't care, but arg gets expanded
								 * anyway */
	status = krb_recvauth(krbopts,
89
						  port->sock,
90 91 92
						  &clttkt,
						  PG_KRB_SRVNAM,
						  instance,
93 94
						  &port->raddr.in,
						  &port->laddr.in,
95 96 97 98 99 100
						  &auth_data,
						  PG_KRB_SRVTAB,
						  key_sched,
						  version);
	if (status != KSUCCESS)
	{
M
 
Marc G. Fournier 已提交
101
		snprintf(PQerrormsg, ERROR_MSG_LENGTH,
B
Bruce Momjian 已提交
102
		  "pg_krb4_recvauth: kerberos error: %s\n", krb_err_txt[status]);
103 104
		fputs(PQerrormsg, stderr);
		pqdebug("%s", PQerrormsg);
105
		return STATUS_ERROR;
106 107 108
	}
	if (strncmp(version, PG_KRB4_VERSION, KRB_SENDAUTH_VLEN))
	{
M
 
Marc G. Fournier 已提交
109
		snprintf(PQerrormsg, ERROR_MSG_LENGTH,
B
Bruce Momjian 已提交
110
				 "pg_krb4_recvauth: protocol version != \"%s\"\n", PG_KRB4_VERSION);
111 112
		fputs(PQerrormsg, stderr);
		pqdebug("%s", PQerrormsg);
113
		return STATUS_ERROR;
114
	}
115
	if (strncmp(port->user, auth_data.pname, SM_USER))
116
	{
M
 
Marc G. Fournier 已提交
117
		snprintf(PQerrormsg, ERROR_MSG_LENGTH,
B
Bruce Momjian 已提交
118 119
				 "pg_krb4_recvauth: name \"%s\" != \"%s\"\n",
				 port->user, auth_data.pname);
120 121
		fputs(PQerrormsg, stderr);
		pqdebug("%s", PQerrormsg);
122
		return STATUS_ERROR;
123
	}
124
	return STATUS_OK;
125 126
}

127 128
#else
static int
129
pg_krb4_recvauth(Port *port)
130
{
M
 
Marc G. Fournier 已提交
131
	snprintf(PQerrormsg, ERROR_MSG_LENGTH,
B
Bruce Momjian 已提交
132
		 "pg_krb4_recvauth: Kerberos not implemented on this server.\n");
133 134
	fputs(PQerrormsg, stderr);
	pqdebug("%s", PQerrormsg);
135

136
	return STATUS_ERROR;
137
}
138

139
#endif	 /* KRB4 */
140

141

142
#ifdef KRB5
143 144 145
/* This needs to be ifdef'd out because krb5.h doesn't exist.  This needs
   to be fixed.
*/
146 147 148 149 150
/*----------------------------------------------------------------
 * MIT Kerberos authentication system - protocol version 5
 *----------------------------------------------------------------
 */

M
Marc G. Fournier 已提交
151
#include <krb5/krb5.h>
152 153 154

/*
 * pg_an_to_ln -- return the local name corresponding to an authentication
155
 *				  name
156 157
 *
 * XXX Assumes that the first aname component is the user name.  This is NOT
158 159 160 161 162 163 164
 *	   necessarily so, since an aname can actually be something out of your
 *	   worst X.400 nightmare, like
 *		  ORGANIZATION=U. C. Berkeley/NAME=Paul M. Aoki@CS.BERKELEY.EDU
 *	   Note that the MIT an_to_ln code does the same thing if you don't
 *	   provide an aname mapping database...it may be a better idea to use
 *	   krb5_an_to_ln, except that it punts if multiple components are found,
 *	   and we can't afford to punt.
165
 */
166
static char *
167 168
pg_an_to_ln(char *aname)
{
169
	char	   *p;
170 171 172

	if ((p = strchr(aname, '/')) || (p = strchr(aname, '@')))
		*p = '\0';
173
	return aname;
174 175 176
}

/*
177
 * pg_krb5_recvauth -- server routine to receive authentication information
178
 *					   from the client
179 180
 *
 * We still need to compare the username obtained from the client's setup
181
 * packet to the authenticated name, as described in pg_krb4_recvauth.	This
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198
 * is a bit more problematic in v5, as described above in pg_an_to_ln.
 *
 * In addition, as described above in pg_krb5_sendauth, we still need to
 * canonicalize the server name v4-style before constructing a principal
 * from it.  Again, this is kind of iffy.
 *
 * Finally, we need to tangle with the fact that v5 doesn't let you explicitly
 * set server keytab file names -- you have to feed lower-level routines a
 * function to retrieve the contents of a keytab, along with a single argument
 * that allows them to open the keytab.  We assume that a server keytab is
 * always a real file so we can allow people to specify their own filenames.
 * (This is important because the POSTGRES keytab needs to be readable by
 * non-root users/groups; the v4 tools used to force you do dump a whole
 * host's worth of keys into a file, effectively forcing you to use one file,
 * but kdb5_edit allows you to select which principals to dump.  Yay!)
 */
static int
199
pg_krb5_recvauth(Port *port)
200
{
201 202 203 204
	char		servbuf[MAXHOSTNAMELEN + 1 +
									sizeof(PG_KRB_SRVNAM)];
	char	   *hostp,
			   *kusername = (char *) NULL;
205
	krb5_error_code code;
206 207 208
	krb5_principal client,
				server;
	krb5_address sender_addr;
209
	krb5_rdreq_key_proc keyproc = (krb5_rdreq_key_proc) NULL;
210
	krb5_pointer keyprocarg = (krb5_pointer) NULL;
211 212 213 214 215 216 217 218 219 220 221 222 223 224

	/*
	 * Set up server side -- since we have no ticket file to make this
	 * easy, we construct our own name and parse it.  See note on
	 * canonicalization above.
	 */
	strcpy(servbuf, PG_KRB_SRVNAM);
	*(hostp = servbuf + (sizeof(PG_KRB_SRVNAM) - 1)) = '/';
	if (gethostname(++hostp, MAXHOSTNAMELEN) < 0)
		strcpy(hostp, "localhost");
	if (hostp = strchr(hostp, '.'))
		*hostp = '\0';
	if (code = krb5_parse_name(servbuf, &server))
	{
M
 
Marc G. Fournier 已提交
225
		snprintf(PQerrormsg, ERROR_MSG_LENGTH,
B
Bruce Momjian 已提交
226
		"pg_krb5_recvauth: Kerberos error %d in krb5_parse_name\n", code);
227
		com_err("pg_krb5_recvauth", code, "in krb5_parse_name");
228
		return STATUS_ERROR;
229 230 231 232 233 234
	}

	/*
	 * krb5_sendauth needs this to verify the address in the client
	 * authenticator.
	 */
235 236 237
	sender_addr.addrtype = port->raddr.in.sin_family;
	sender_addr.length = sizeof(port->raddr.in.sin_addr);
	sender_addr.contents = (krb5_octet *) & (port->raddr.in.sin_addr);
238 239 240 241 242 243 244

	if (strcmp(PG_KRB_SRVTAB, ""))
	{
		keyproc = krb5_kt_read_service_key;
		keyprocarg = PG_KRB_SRVTAB;
	}

245
	if (code = krb5_recvauth((krb5_pointer) & port->sock,
246 247 248 249 250 251 252 253 254 255 256 257
							 PG_KRB5_VERSION,
							 server,
							 &sender_addr,
							 (krb5_pointer) NULL,
							 keyproc,
							 keyprocarg,
							 (char *) NULL,
							 (krb5_int32 *) NULL,
							 &client,
							 (krb5_ticket **) NULL,
							 (krb5_authenticator **) NULL))
	{
M
 
Marc G. Fournier 已提交
258
		snprintf(PQerrormsg, ERROR_MSG_LENGTH,
B
Bruce Momjian 已提交
259
		 "pg_krb5_recvauth: Kerberos error %d in krb5_recvauth\n", code);
260 261
		com_err("pg_krb5_recvauth", code, "in krb5_recvauth");
		krb5_free_principal(server);
262
		return STATUS_ERROR;
263
	}
264
	krb5_free_principal(server);
265 266 267 268 269 270 271 272

	/*
	 * The "client" structure comes out of the ticket and is therefore
	 * authenticated.  Use it to check the username obtained from the
	 * postmaster startup packet.
	 */
	if ((code = krb5_unparse_name(client, &kusername)))
	{
M
 
Marc G. Fournier 已提交
273
		snprintf(PQerrormsg, ERROR_MSG_LENGTH,
B
Bruce Momjian 已提交
274
				 "pg_krb5_recvauth: Kerberos error %d in krb5_unparse_name\n", code);
275 276
		com_err("pg_krb5_recvauth", code, "in krb5_unparse_name");
		krb5_free_principal(client);
277
		return STATUS_ERROR;
278
	}
279
	krb5_free_principal(client);
280 281
	if (!kusername)
	{
M
 
Marc G. Fournier 已提交
282
		snprintf(PQerrormsg, ERROR_MSG_LENGTH,
B
Bruce Momjian 已提交
283
				 "pg_krb5_recvauth: could not decode username\n");
284 285
		fputs(PQerrormsg, stderr);
		pqdebug("%s", PQerrormsg);
286
		return STATUS_ERROR;
287 288
	}
	kusername = pg_an_to_ln(kusername);
289
	if (strncmp(username, kusername, SM_USER))
290
	{
M
 
Marc G. Fournier 已提交
291
		snprintf(PQerrormsg, ERROR_MSG_LENGTH,
B
Bruce Momjian 已提交
292
				 "pg_krb5_recvauth: name \"%s\" != \"%s\"\n", port->user, kusername);
293 294
		fputs(PQerrormsg, stderr);
		pqdebug("%s", PQerrormsg);
295
		pfree(kusername);
296
		return STATUS_ERROR;
297
	}
298
	pfree(kusername);
299
	return STATUS_OK;
300 301
}

302
#else
303
static int
304
pg_krb5_recvauth(Port *port)
305
{
M
 
Marc G. Fournier 已提交
306
	snprintf(PQerrormsg, ERROR_MSG_LENGTH,
B
Bruce Momjian 已提交
307
		 "pg_krb5_recvauth: Kerberos not implemented on this server.\n");
308 309
	fputs(PQerrormsg, stderr);
	pqdebug("%s", PQerrormsg);
310

311
	return STATUS_ERROR;
312
}
313

314
#endif	 /* KRB5 */
315

316 317 318 319 320

/*
 * Handle a v0 password packet.
 */

M
 
Marc G. Fournier 已提交
321 322
static int
pg_passwordv0_recvauth(void *arg, PacketLen len, void *pkt)
323
{
324
	Port	   *port;
325
	PasswordPacketV0 *pp;
326 327 328 329
	char	   *user,
			   *password,
			   *cp,
			   *start;
330

331 332
	port = (Port *) arg;
	pp = (PasswordPacketV0 *) pkt;
333 334 335 336 337 338 339 340

	/*
	 * The packet is supposed to comprise the user name and the password
	 * as C strings.  Be careful the check that this is the case.
	 */

	user = password = NULL;

341
	len -= sizeof(pp->unused);
342 343 344

	cp = start = pp->data;

345 346
	while (len-- > 0)
		if (*cp++ == '\0')
347
		{
348 349 350 351 352 353 354
			if (user == NULL)
				user = start;
			else
			{
				password = start;
				break;
			}
355

356 357
			start = cp;
		}
358 359

	if (user == NULL || password == NULL)
360
	{
M
 
Marc G. Fournier 已提交
361
		snprintf(PQerrormsg, ERROR_MSG_LENGTH,
B
Bruce Momjian 已提交
362
				 "pg_password_recvauth: badly formed password packet.\n");
363 364
		fputs(PQerrormsg, stderr);
		pqdebug("%s", PQerrormsg);
365 366

		auth_failed(port);
367
	}
368 369
	else
	{
370 371
		int			status;
		UserAuth	saved;
372 373 374 375 376 377 378 379 380 381 382 383 384 385 386

		/* Check the password. */

		saved = port->auth_method;
		port->auth_method = uaPassword;

		status = checkPassword(port, user, password);

		port->auth_method = saved;

		/* Adjust the result if necessary. */

		if (map_old_to_new(port, uaPassword, status) != STATUS_OK)
			auth_failed(port);
	}
M
 
Marc G. Fournier 已提交
387

388
	return STATUS_OK;			/* don't close the connection yet */
389
}
390 391


392
/*
393 394 395 396 397 398 399 400 401 402
 * Tell the user the authentication failed, but not (much about) why.
 *
 * There is a tradeoff here between security concerns and making life
 * unnecessarily difficult for legitimate users.  We would not, for example,
 * want to report the password we were expecting to receive...
 * But it seems useful to report the username and authorization method
 * in use, and these are items that must be presumed known to an attacker
 * anyway.
 * Note that many sorts of failure report additional information in the
 * postmaster log, which we hope is only readable by good guys.
403
 */
404

405 406
void
auth_failed(Port *port)
407
{
B
Bruce Momjian 已提交
408
	char		buffer[512];
409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439
	const char *authmethod = "Unknown auth method:";

	switch (port->auth_method)
	{
		case uaReject:
			authmethod = "Rejected host:";
			break;
		case uaKrb4:
			authmethod = "Kerberos4";
			break;
		case uaKrb5:
			authmethod = "Kerberos5";
			break;
		case uaTrust:
			authmethod = "Trusted";
			break;
		case uaIdent:
			authmethod = "IDENT";
			break;
		case uaPassword:
			authmethod = "Password";
			break;
		case uaCrypt:
			authmethod = "Password";
			break;
	}

	sprintf(buffer, "%s authentication failed for user '%s'",
			authmethod, port->user);

	PacketSendError(&port->pktInfo, buffer);
440 441
}

442

443 444 445
/*
 * be_recvauth -- server demux routine for incoming authentication information
 */
446 447
void
be_recvauth(Port *port)
448
{
449

450
	/*
451
	 * Get the authentication method to use for this frontend/database
B
Bruce Momjian 已提交
452 453 454
	 * combination.  Note: a failure return indicates a problem with the
	 * hba config file, not with the request.  hba.c should have dropped
	 * an error message into the postmaster logfile if it failed.
455
	 */
456

457
	if (hba_getauthmethod(&port->raddr, port->user, port->database,
458
						port->auth_arg, &port->auth_method) != STATUS_OK)
459 460
		PacketSendError(&port->pktInfo,
						"Missing or erroneous pg_hba.conf file, see postmaster log for details");
461

462
	else if (PG_PROTOCOL_MAJOR(port->proto) == 0)
463
	{
464 465
		/* Handle old style authentication. */

466 467
		if (old_be_recvauth(port) != STATUS_OK)
			auth_failed(port);
468
	}
469
	else
470
	{
471 472
		/* Handle new style authentication. */

B
Bruce Momjian 已提交
473 474
		AuthRequest areq = AUTH_REQ_OK;
		PacketDoneProc auth_handler = NULL;
475 476

		switch (port->auth_method)
477
		{
478
			case uaReject:
B
Bruce Momjian 已提交
479

480
				/*
B
Bruce Momjian 已提交
481 482 483 484 485 486
				 * This could have come from an explicit "reject" entry in
				 * pg_hba.conf, but more likely it means there was no
				 * matching entry.	Take pity on the poor user and issue a
				 * helpful error message.  NOTE: this is not a security
				 * breach, because all the info reported here is known at
				 * the frontend and must be assumed known to bad guys.
487 488 489
				 * We're merely helping out the less clueful good guys.
				 * NOTE 2: libpq-be.h defines the maximum error message
				 * length as 99 characters.  It probably wouldn't hurt
B
Bruce Momjian 已提交
490 491
				 * anything to increase it, but there might be some client
				 * out there that will fail.  So, be terse.
492 493
				 */
				{
B
Bruce Momjian 已提交
494
					char		buffer[512];
495 496 497 498 499 500 501 502 503 504
					const char *hostinfo = "localhost";

					if (port->raddr.sa.sa_family == AF_INET)
						hostinfo = inet_ntoa(port->raddr.in.sin_addr);
					sprintf(buffer,
							"No pg_hba.conf entry for host %s, user %s, database %s",
							hostinfo, port->user, port->database);
					PacketSendError(&port->pktInfo, buffer);
					return;
				}
505
				break;
506

507 508 509 510
			case uaKrb4:
				areq = AUTH_REQ_KRB4;
				auth_handler = handle_krb4_auth;
				break;
511

512 513 514 515
			case uaKrb5:
				areq = AUTH_REQ_KRB5;
				auth_handler = handle_krb5_auth;
				break;
516

517
			case uaTrust:
518 519
				areq = AUTH_REQ_OK;
				auth_handler = handle_done_auth;
520
				break;
521

522 523 524 525 526 527 528
			case uaIdent:
				if (authident(&port->raddr.in, &port->laddr.in,
							  port->user, port->auth_arg) == STATUS_OK)
				{
					areq = AUTH_REQ_OK;
					auth_handler = handle_done_auth;
				}
529

530
				break;
531

532 533 534 535 536 537 538 539 540 541
			case uaPassword:
				areq = AUTH_REQ_PASSWORD;
				auth_handler = handle_password_auth;
				break;

			case uaCrypt:
				areq = AUTH_REQ_CRYPT;
				auth_handler = handle_password_auth;
				break;
		}
542 543 544 545 546 547 548 549

		/* Tell the frontend what we want next. */

		if (auth_handler != NULL)
			sendAuthRequest(port, areq, auth_handler);
		else
			auth_failed(port);
	}
550
}
551

552 553

/*
554
 * Send an authentication request packet to the frontend.
555
 */
556

557
static void
M
 
Marc G. Fournier 已提交
558
sendAuthRequest(Port *port, AuthRequest areq, PacketDoneProc handler)
559
{
560 561 562 563
	char	   *dp,
			   *sp;
	int			i;
	uint32		net_areq;
564

565 566 567 568 569
	/* Convert to a byte stream. */

	net_areq = htonl(areq);

	dp = port->pktInfo.pkt.ar.data;
570
	sp = (char *) &net_areq;
571 572 573 574 575 576 577 578 579

	*dp++ = 'R';

	for (i = 1; i <= 4; ++i)
		*dp++ = *sp++;

	/* Add the salt for encrypted passwords. */

	if (areq == AUTH_REQ_CRYPT)
580
	{
581 582 583
		*dp++ = port->salt[0];
		*dp++ = port->salt[1];
		i += 2;
584
	}
585

M
 
Marc G. Fournier 已提交
586
	PacketSendSetup(&port->pktInfo, i, handler, (void *) port);
587 588 589 590 591 592 593
}


/*
 * Called when we have told the front end that it is authorised.
 */

M
 
Marc G. Fournier 已提交
594 595
static int
handle_done_auth(void *arg, PacketLen len, void *pkt)
596
{
597

598 599 600 601 602
	/*
	 * Don't generate any more traffic.  This will cause the backend to
	 * start.
	 */

M
 
Marc G. Fournier 已提交
603
	return STATUS_OK;
604 605 606 607 608 609 610 611
}


/*
 * Called when we have told the front end that it should use Kerberos V4
 * authentication.
 */

M
 
Marc G. Fournier 已提交
612 613
static int
handle_krb4_auth(void *arg, PacketLen len, void *pkt)
614
{
M
 
Marc G. Fournier 已提交
615 616
	Port	   *port = (Port *) arg;

617 618 619 620
	if (pg_krb4_recvauth(port) != STATUS_OK)
		auth_failed(port);
	else
		sendAuthRequest(port, AUTH_REQ_OK, handle_done_auth);
M
 
Marc G. Fournier 已提交
621 622

	return STATUS_OK;
623 624 625 626 627 628 629 630
}


/*
 * Called when we have told the front end that it should use Kerberos V5
 * authentication.
 */

M
 
Marc G. Fournier 已提交
631 632
static int
handle_krb5_auth(void *arg, PacketLen len, void *pkt)
633
{
M
 
Marc G. Fournier 已提交
634 635
	Port	   *port = (Port *) arg;

636 637 638 639
	if (pg_krb5_recvauth(port) != STATUS_OK)
		auth_failed(port);
	else
		sendAuthRequest(port, AUTH_REQ_OK, handle_done_auth);
M
 
Marc G. Fournier 已提交
640 641

	return STATUS_OK;
642 643 644 645 646 647 648 649
}


/*
 * Called when we have told the front end that it should use password
 * authentication.
 */

M
 
Marc G. Fournier 已提交
650 651
static int
handle_password_auth(void *arg, PacketLen len, void *pkt)
652
{
M
 
Marc G. Fournier 已提交
653 654
	Port	   *port = (Port *) arg;

655 656
	/* Set up the read of the password packet. */

M
 
Marc G. Fournier 已提交
657 658 659
	PacketReceiveSetup(&port->pktInfo, readPasswordPacket, (void *) port);

	return STATUS_OK;
660 661 662 663 664 665 666
}


/*
 * Called when we have received the password packet.
 */

M
 
Marc G. Fournier 已提交
667 668
static int
readPasswordPacket(void *arg, PacketLen len, void *pkt)
669
{
670
	char		password[sizeof(PasswordPacket) + 1];
M
 
Marc G. Fournier 已提交
671
	Port	   *port = (Port *) arg;
672 673 674

	/* Silently truncate a password that is too big. */

675 676 677 678
	if (len > sizeof(PasswordPacket))
		len = sizeof(PasswordPacket);

	StrNCpy(password, ((PasswordPacket *) pkt)->passwd, len);
679

680
	if (checkPassword(port, port->user, password) != STATUS_OK)
681 682 683
		auth_failed(port);
	else
		sendAuthRequest(port, AUTH_REQ_OK, handle_done_auth);
M
 
Marc G. Fournier 已提交
684

685
	return STATUS_OK;			/* don't close the connection yet */
686 687
}

688

689 690
/*
 * Use the local flat password file if clear passwords are used and the file is
691
 * specified.  Otherwise use the password in the pg_shadow table, encrypted or
692 693 694
 * not.
 */

695 696
static int
checkPassword(Port *port, char *user, char *password)
697 698 699 700 701 702 703 704
{
	if (port->auth_method == uaPassword && port->auth_arg[0] != '\0')
		return verify_password(port->auth_arg, user, password);

	return crypt_verify(port, user, password);
}


705 706 707 708
/*
 * Server demux routine for incoming authentication information for protocol
 * version 0.
 */
709 710
static int
old_be_recvauth(Port *port)
711
{
712 713
	int			status;
	MsgType		msgtype = (MsgType) port->proto;
714 715 716 717

	/* Handle the authentication that's offered. */

	switch (msgtype)
718 719 720 721
	{
		case STARTUP_KRB4_MSG:
			status = map_old_to_new(port, uaKrb4, pg_krb4_recvauth(port));
			break;
722

723 724 725
		case STARTUP_KRB5_MSG:
			status = map_old_to_new(port, uaKrb5, pg_krb5_recvauth(port));
			break;
726

727 728 729
		case STARTUP_MSG:
			status = map_old_to_new(port, uaTrust, STATUS_OK);
			break;
730

731 732
		case STARTUP_PASSWORD_MSG:
			PacketReceiveSetup(&port->pktInfo, pg_passwordv0_recvauth,
M
 
Marc G. Fournier 已提交
733
							   (void *) port);
734

735
			return STATUS_OK;
736

737 738
		default:
			fprintf(stderr, "Invalid startup message type: %u\n", msgtype);
739

740 741
			return STATUS_OK;
	}
742 743 744

	return status;
}
745

746 747

/*
748
 * The old style authentication has been done.	Modify the result of this (eg.
749 750 751 752
 * allow the connection anyway, disallow it anyway, or use the result)
 * depending on what authentication we really want to use.
 */

753 754
static int
map_old_to_new(Port *port, UserAuth old, int status)
755 756 757
{
	switch (port->auth_method)
	{
758 759
			case uaCrypt:
			case uaReject:
760
			status = STATUS_ERROR;
761
			break;
762

763 764 765 766
		case uaKrb4:
			if (old != uaKrb4)
				status = STATUS_ERROR;
			break;
767

768 769 770 771
		case uaKrb5:
			if (old != uaKrb5)
				status = STATUS_ERROR;
			break;
772

773 774 775
		case uaTrust:
			status = STATUS_OK;
			break;
776

777 778 779 780 781 782 783 784
		case uaIdent:
			status = authident(&port->raddr.in, &port->laddr.in,
							   port->user, port->auth_arg);
			break;

		case uaPassword:
			if (old != uaPassword)
				status = STATUS_ERROR;
785

786
			break;
787
	}
788

789
	return status;
790
}