88 *
99 *
1010 * IDENTIFICATION
11- * $Header: /cvsroot/pgsql/src/backend/libpq/auth.c,v 1.44 2000/04/12 17:15:13 momjian Exp $
11+ * $Header: /cvsroot/pgsql/src/backend/libpq/auth.c,v 1.45 2000/05/27 03:39:31 momjian Exp $
1212 *
1313 *-------------------------------------------------------------------------
1414 */
@@ -149,7 +149,8 @@ pg_krb4_recvauth(Port *port)
149149 *----------------------------------------------------------------
150150 */
151151
152- #include "krb5/krb5.h"
152+ #include <krb5.h>
153+ #include <com_err.h>
153154
154155/*
155156 * pg_an_to_ln -- return the local name corresponding to an authentication
@@ -174,130 +175,134 @@ pg_an_to_ln(char *aname)
174175return aname ;
175176}
176177
178+
177179/*
178- * pg_krb5_recvauth -- server routine to receive authentication information
179- * from the client
180- *
181- * We still need to compare the username obtained from the client's setup
182- * packet to the authenticated name, as described in pg_krb4_recvauth.This
183- * is a bit more problematic in v5, as described above in pg_an_to_ln.
184- *
185- * In addition, as described above in pg_krb5_sendauth, we still need to
186- * canonicalize the server name v4-style before constructing a principal
187- * from it. Again, this is kind of iffy.
188- *
189- * Finally, we need to tangle with the fact that v5 doesn't let you explicitly
190- * set server keytab file names -- you have to feed lower-level routines a
191- * function to retrieve the contents of a keytab, along with a single argument
192- * that allows them to open the keytab. We assume that a server keytab is
193- * always a real file so we can allow people to specify their own filenames.
194- * (This is important because the POSTGRES keytab needs to be readable by
195- * non-root users/groups; the v4 tools used to force you do dump a whole
196- * host's worth of keys into a file, effectively forcing you to use one file,
197- * but kdb5_edit allows you to select which principals to dump. Yay!)
180+ * Various krb5 state which is not connection specfic, and a flag to
181+ * indicate whether we have initialised it yet.
198182 */
183+ static int pg_krb5_initialised ;
184+ static krb5_context pg_krb5_context ;
185+ static krb5_keytab pg_krb5_keytab ;
186+ static krb5_principal pg_krb5_server ;
187+
188+
199189static int
200- pg_krb5_recvauth ( Port * port )
190+ pg_krb5_init ( void )
201191{
202- char servbuf [MAXHOSTNAMELEN + 1 +
203- sizeof (PG_KRB_SRVNAM )];
204- char * hostp ,
205- * kusername = (char * )NULL ;
206- krb5_error_code code ;
207- krb5_principal client ,
208- server ;
209- krb5_address sender_addr ;
210- krb5_rdreq_key_proc keyproc = (krb5_rdreq_key_proc )NULL ;
211- krb5_pointer keyprocarg = (krb5_pointer )NULL ;
192+ krb5_error_code retval ;
212193
213- /*
214- * Set up server side -- since we have no ticket file to make this
215- * easy, we construct our own name and parse it. See note on
216- * canonicalization above.
217- */
218- strcpy (servbuf ,PG_KRB_SRVNAM );
219- * (hostp = servbuf + (sizeof (PG_KRB_SRVNAM )- 1 ))= '/' ;
220- if (gethostname (++ hostp ,MAXHOSTNAMELEN )< 0 )
221- strcpy (hostp ,"localhost" );
222- if (hostp = strchr (hostp ,'.' ))
223- * hostp = '\0' ;
224- if (code = krb5_parse_name (servbuf ,& server ))
225- {
194+ if (pg_krb5_initialised )
195+ return STATUS_OK ;
196+
197+ retval = krb5_init_context (& pg_krb5_context );
198+ if (retval ) {
226199snprintf (PQerrormsg ,PQERRORMSG_LENGTH ,
227- "pg_krb5_recvauth: Kerberos error %d in krb5_parse_name\n" ,code );
228- com_err ("pg_krb5_recvauth" ,code ,"in krb5_parse_name" );
200+ "pg_krb5_init: krb5_init_context returned"
201+ " Kerberos error %d\n" ,retval );
202+ com_err ("postgres" ,retval ,"while initializing krb5" );
229203return STATUS_ERROR ;
230204}
231205
232- /*
233- * krb5_sendauth needs this to verify the address in the client
234- * authenticator.
235- */
236- sender_addr .addrtype = port -> raddr .in .sin_family ;
237- sender_addr .length = sizeof (port -> raddr .in .sin_addr );
238- sender_addr .contents = (krb5_octet * )& (port -> raddr .in .sin_addr );
239-
240- if (strcmp (PG_KRB_SRVTAB ,"" ))
241- {
242- keyproc = krb5_kt_read_service_key ;
243- keyprocarg = PG_KRB_SRVTAB ;
206+ retval = krb5_kt_resolve (pg_krb5_context ,PG_KRB_SRVTAB ,& pg_krb5_keytab );
207+ if (retval ) {
208+ snprintf (PQerrormsg ,PQERRORMSG_LENGTH ,
209+ "pg_krb5_init: krb5_kt_resolve returned"
210+ " Kerberos error %d\n" ,retval );
211+ com_err ("postgres" ,retval ,"while resolving keytab file %s" ,
212+ PG_KRB_SRVTAB );
213+ krb5_free_context (pg_krb5_context );
214+ return STATUS_ERROR ;
244215}
245216
246- if (code = krb5_recvauth ((krb5_pointer )& port -> sock ,
247- PG_KRB5_VERSION ,
248- server ,
249- & sender_addr ,
250- (krb5_pointer )NULL ,
251- keyproc ,
252- keyprocarg ,
253- (char * )NULL ,
254- (krb5_int32 * )NULL ,
255- & client ,
256- (krb5_ticket * * )NULL ,
257- (krb5_authenticator * * )NULL ))
258- {
217+ retval = krb5_sname_to_principal (pg_krb5_context ,NULL ,PG_KRB_SRVNAM ,
218+ KRB5_NT_SRV_HST ,& pg_krb5_server );
219+ if (retval ) {
259220snprintf (PQerrormsg ,PQERRORMSG_LENGTH ,
260- "pg_krb5_recvauth: Kerberos error %d in krb5_recvauth\n" ,code );
261- com_err ("pg_krb5_recvauth" ,code ,"in krb5_recvauth" );
262- krb5_free_principal (server );
221+ "pg_krb5_init: krb5_sname_to_principal returned"
222+ " Kerberos error %d\n" ,retval );
223+ com_err ("postgres" ,retval ,
224+ "while getting server principal for service %s" ,
225+ PG_KRB_SRVTAB );
226+ krb5_kt_close (pg_krb5_context ,pg_krb5_keytab );
227+ krb5_free_context (pg_krb5_context );
263228return STATUS_ERROR ;
264229}
265- krb5_free_principal (server );
230+
231+ pg_krb5_initialised = 1 ;
232+ return STATUS_OK ;
233+ }
234+
235+
236+ /*
237+ * pg_krb5_recvauth -- server routine to receive authentication information
238+ * from the client
239+ *
240+ * We still need to compare the username obtained from the client's setup
241+ * packet to the authenticated name, as described in pg_krb4_recvauth.This
242+ * is a bit more problematic in v5, as described above in pg_an_to_ln.
243+ *
244+ * We have our own keytab file because postgres is unlikely to run as root,
245+ * and so cannot read the default keytab.
246+ */
247+ static int
248+ pg_krb5_recvauth (Port * port )
249+ {
250+ krb5_error_code retval ;
251+ int ret ;
252+ krb5_auth_context auth_context = NULL ;
253+ krb5_ticket * ticket ;
254+ char * kusername ;
255+
256+ ret = pg_krb5_init ();
257+ if (ret != STATUS_OK )
258+ return ret ;
259+
260+ retval = krb5_recvauth (pg_krb5_context ,& auth_context ,
261+ (krb5_pointer )& port -> sock ,PG_KRB_SRVNAM ,
262+ pg_krb5_server ,0 ,pg_krb5_keytab ,& ticket );
263+ if (retval ) {
264+ snprintf (PQerrormsg ,PQERRORMSG_LENGTH ,
265+ "pg_krb5_recvauth: krb5_recvauth returned"
266+ " Kerberos error %d\n" ,retval );
267+ com_err ("postgres" ,retval ,"from krb5_recvauth" );
268+ return STATUS_ERROR ;
269+ }
266270
267271/*
268272 * The "client" structure comes out of the ticket and is therefore
269273 * authenticated. Use it to check the username obtained from the
270274 * postmaster startup packet.
275+ *
276+ * I have no idea why this is considered necessary.
271277 */
272- if ((code = krb5_unparse_name (client ,& kusername )))
273- {
278+ retval = krb5_unparse_name (pg_krb5_context ,
279+ ticket -> enc_part2 -> client ,& kusername );
280+ if (retval ) {
274281snprintf (PQerrormsg ,PQERRORMSG_LENGTH ,
275- "pg_krb5_recvauth: Kerberos error %d in krb5_unparse_name\n" ,code );
276- com_err ("pg_krb5_recvauth" ,code ,"in krb5_unparse_name" );
277- krb5_free_principal (client );
278- return STATUS_ERROR ;
279- }
280- krb5_free_principal (client );
281- if (!kusername )
282- {
283- snprintf (PQerrormsg ,PQERRORMSG_LENGTH ,
284- "pg_krb5_recvauth: could not decode username\n" );
285- fputs (PQerrormsg ,stderr );
286- pqdebug ("%s" ,PQerrormsg );
282+ "pg_krb5_recvauth: krb5_unparse_name returned"
283+ " Kerberos error %d\n" ,retval );
284+ com_err ("postgres" ,retval ,"while unparsing client name" );
285+ krb5_free_ticket (pg_krb5_context ,ticket );
286+ krb5_auth_con_free (pg_krb5_context ,auth_context );
287287return STATUS_ERROR ;
288288}
289+
289290kusername = pg_an_to_ln (kusername );
290- if (strncmp (username ,kusername ,SM_USER ))
291+ if (strncmp (port -> user ,kusername ,SM_USER ))
291292{
292293snprintf (PQerrormsg ,PQERRORMSG_LENGTH ,
293- "pg_krb5_recvauth: name \"%s\" != \"%s\"\n" ,port -> user ,kusername );
294- fputs (PQerrormsg ,stderr );
295- pqdebug ("%s" ,PQerrormsg );
296- pfree (kusername );
297- return STATUS_ERROR ;
294+ "pg_krb5_recvauth: user name \"%s\" != krb5 name \"%s\"\n" ,
295+ port -> user ,kusername );
296+ ret = STATUS_ERROR ;
298297}
299- pfree (kusername );
300- return STATUS_OK ;
298+ else
299+ ret = STATUS_OK ;
300+
301+ krb5_free_ticket (pg_krb5_context ,ticket );
302+ krb5_auth_con_free (pg_krb5_context ,auth_context );
303+ free (kusername );
304+
305+ return ret ;
301306}
302307
303308#else