Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit9ead05b

Browse files
committed
Prevent PL/Tcl from loading the "unknown" module from pltcl_modules unless
that is a regular table or view owned by a superuser. This prevents atrojan horse attack whereby any unprivileged SQL user could create such atable and insert code into it that would then get executed in other users'sessions whenever they call pltcl functions.Worse yet, because the code was automatically loaded into both the "normal"and "safe" interpreters at first use, the attacker could execute unrestrictedTcl code in the "normal" interpreter without there being any pltclu functionsanywhere, or indeed anyone else using pltcl at all: installing pltcl issufficient to open the hole. Change the initialization logic so that the"unknown" code is only loaded into an interpreter when the interpreter isfirst really used. (That doesn't add any additional security in thisparticular context, but it seems a prudent change, and anyway the formerbehavior violated the principle of least astonishment.)Security:CVE-2010-1170
1 parent1f474d2 commit9ead05b

File tree

2 files changed

+114
-65
lines changed

2 files changed

+114
-65
lines changed

‎doc/src/sgml/pltcl.sgml

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<!-- $PostgreSQL: pgsql/doc/src/sgml/pltcl.sgml,v 2.49 2010/04/03 07:22:55 petere Exp $ -->
1+
<!-- $PostgreSQL: pgsql/doc/src/sgml/pltcl.sgml,v 2.50 2010/05/13 18:29:12 tgl Exp $ -->
22

33
<chapter id="pltcl">
44
<title>PL/Tcl - Tcl Procedural Language</title>
@@ -689,8 +689,10 @@ CREATE TRIGGER trig_mytab_modcount BEFORE INSERT OR UPDATE ON mytab
689689
It recognizes a special table, <literal>pltcl_modules</>, which
690690
is presumed to contain modules of Tcl code. If this table
691691
exists, the module <literal>unknown</> is fetched from the table
692-
and loaded into the Tcl interpreter immediately after creating
693-
the interpreter.
692+
and loaded into the Tcl interpreter immediately before the first
693+
execution of a PL/Tcl function in a database session. (This
694+
happens separately for PL/Tcl and PL/TclU, if both are used,
695+
because separate interpreters are used for the two languages.)
694696
</para>
695697
<para>
696698
While the <literal>unknown</> module could actually contain any
@@ -717,7 +719,11 @@ CREATE TRIGGER trig_mytab_modcount BEFORE INSERT OR UPDATE ON mytab
717719
<para>
718720
The tables <literal>pltcl_modules</> and <literal>pltcl_modfuncs</>
719721
must be readable by all, but it is wise to make them owned and
720-
writable only by the database administrator.
722+
writable only by the database administrator. As a security
723+
precaution, PL/Tcl will ignore <literal>pltcl_modules</> (and thus,
724+
not attempt to load the <literal>unknown</> module) unless it is
725+
owned by a superuser. But update privileges on this table can be
726+
granted to other users, if you trust them sufficiently.
721727
</para>
722728
</sect1>
723729

‎src/pl/tcl/pltcl.c

Lines changed: 104 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* pltcl.c- PostgreSQL support for Tcl as
33
* procedural language (PL)
44
*
5-
* $PostgreSQL: pgsql/src/pl/tcl/pltcl.c,v 1.132 2010/02/26 02:01:37 momjian Exp $
5+
* $PostgreSQL: pgsql/src/pl/tcl/pltcl.c,v 1.133 2010/05/13 18:29:12 tgl Exp $
66
*
77
**********************************************************************/
88

@@ -120,7 +120,8 @@ typedef struct pltcl_query_desc
120120
* Global data
121121
**********************************************************************/
122122
staticboolpltcl_pm_init_done= false;
123-
staticboolpltcl_be_init_done= false;
123+
staticboolpltcl_be_norm_init_done= false;
124+
staticboolpltcl_be_safe_init_done= false;
124125
staticTcl_Interp*pltcl_hold_interp=NULL;
125126
staticTcl_Interp*pltcl_norm_interp=NULL;
126127
staticTcl_Interp*pltcl_safe_interp=NULL;
@@ -139,8 +140,8 @@ Datumpltcl_call_handler(PG_FUNCTION_ARGS);
139140
Datumpltclu_call_handler(PG_FUNCTION_ARGS);
140141
void_PG_init(void);
141142

142-
staticvoidpltcl_init_all(void);
143143
staticvoidpltcl_init_interp(Tcl_Interp*interp);
144+
staticTcl_Interp*pltcl_fetch_interp(boolpltrusted);
144145
staticvoidpltcl_init_load_unknown(Tcl_Interp*interp);
145146

146147
staticDatumpltcl_func_handler(PG_FUNCTION_ARGS);
@@ -334,33 +335,12 @@ _PG_init(void)
334335
pltcl_pm_init_done= true;
335336
}
336337

337-
/**********************************************************************
338-
* pltcl_init_all()- Initialize all
339-
*
340-
* This does initialization that can't be done in the postmaster, and
341-
* hence is not safe to do at library load time.
342-
**********************************************************************/
343-
staticvoid
344-
pltcl_init_all(void)
345-
{
346-
/************************************************************
347-
* Try to load the unknown procedure from pltcl_modules
348-
************************************************************/
349-
if (!pltcl_be_init_done)
350-
{
351-
if (SPI_connect()!=SPI_OK_CONNECT)
352-
elog(ERROR,"SPI_connect failed");
353-
pltcl_init_load_unknown(pltcl_norm_interp);
354-
pltcl_init_load_unknown(pltcl_safe_interp);
355-
if (SPI_finish()!=SPI_OK_FINISH)
356-
elog(ERROR,"SPI_finish failed");
357-
pltcl_be_init_done= true;
358-
}
359-
}
360-
361-
362338
/**********************************************************************
363339
* pltcl_init_interp() - initialize a Tcl interpreter
340+
*
341+
* The work done here must be safe to do in the postmaster process,
342+
* in case the pltcl library is preloaded in the postmaster. Note
343+
* that this is applied separately to the "normal" and "safe" interpreters.
364344
**********************************************************************/
365345
staticvoid
366346
pltcl_init_interp(Tcl_Interp*interp)
@@ -387,6 +367,42 @@ pltcl_init_interp(Tcl_Interp *interp)
387367
pltcl_SPI_lastoid,NULL,NULL);
388368
}
389369

370+
/**********************************************************************
371+
* pltcl_fetch_interp() - fetch the Tcl interpreter to use for a function
372+
*
373+
* This also takes care of any on-first-use initialization required.
374+
* The initialization work done here can't be done in the postmaster, and
375+
* hence is not safe to do at library load time, because it may invoke
376+
* arbitrary user-defined code.
377+
* Note: we assume caller has already connected to SPI.
378+
**********************************************************************/
379+
staticTcl_Interp*
380+
pltcl_fetch_interp(boolpltrusted)
381+
{
382+
Tcl_Interp*interp;
383+
384+
/* On first use, we try to load the unknown procedure from pltcl_modules */
385+
if (pltrusted)
386+
{
387+
interp=pltcl_safe_interp;
388+
if (!pltcl_be_safe_init_done)
389+
{
390+
pltcl_init_load_unknown(interp);
391+
pltcl_be_safe_init_done= true;
392+
}
393+
}
394+
else
395+
{
396+
interp=pltcl_norm_interp;
397+
if (!pltcl_be_norm_init_done)
398+
{
399+
pltcl_init_load_unknown(interp);
400+
pltcl_be_norm_init_done= true;
401+
}
402+
}
403+
404+
returninterp;
405+
}
390406

391407
/**********************************************************************
392408
* pltcl_init_load_unknown()- Load the unknown procedure from
@@ -395,6 +411,11 @@ pltcl_init_interp(Tcl_Interp *interp)
395411
staticvoid
396412
pltcl_init_load_unknown(Tcl_Interp*interp)
397413
{
414+
Relationpmrel;
415+
char*pmrelname,
416+
*nspname;
417+
char*buf;
418+
intbuflen;
398419
intspi_rc;
399420
inttcl_rc;
400421
Tcl_DStringunknown_src;
@@ -404,47 +425,72 @@ pltcl_init_load_unknown(Tcl_Interp *interp)
404425

405426
/************************************************************
406427
* Check if table pltcl_modules exists
428+
*
429+
* We allow the table to be found anywhere in the search_path.
430+
* This is for backwards compatibility. To ensure that the table
431+
* is trustworthy, we require it to be owned by a superuser.
407432
************************************************************/
408-
spi_rc=SPI_execute("select 1 from pg_catalog.pg_class "
409-
"where relname = 'pltcl_modules'",
410-
false,1);
411-
SPI_freetuptable(SPI_tuptable);
412-
if (spi_rc!=SPI_OK_SELECT)
413-
elog(ERROR,"select from pg_class failed");
414-
if (SPI_processed==0)
433+
pmrel=try_relation_openrv(makeRangeVar(NULL,"pltcl_modules",-1),
434+
AccessShareLock);
435+
if (pmrel==NULL)
436+
return;
437+
/* must be table or view, else ignore */
438+
if (!(pmrel->rd_rel->relkind==RELKIND_RELATION||
439+
pmrel->rd_rel->relkind==RELKIND_VIEW))
440+
{
441+
relation_close(pmrel,AccessShareLock);
442+
return;
443+
}
444+
/* must be owned by superuser, else ignore */
445+
if (!superuser_arg(pmrel->rd_rel->relowner))
446+
{
447+
relation_close(pmrel,AccessShareLock);
415448
return;
449+
}
450+
/* get fully qualified table name for use in select command */
451+
nspname=get_namespace_name(RelationGetNamespace(pmrel));
452+
if (!nspname)
453+
elog(ERROR,"cache lookup failed for namespace %u",
454+
RelationGetNamespace(pmrel));
455+
pmrelname=quote_qualified_identifier(nspname,
456+
RelationGetRelationName(pmrel));
416457

417458
/************************************************************
418-
* Read all therow's from it where modname = 'unknown' in
419-
* the order of modseq
459+
* Read all therows from it where modname = 'unknown',
460+
*inthe order of modseq
420461
************************************************************/
421-
Tcl_DStringInit(&unknown_src);
462+
buflen=strlen(pmrelname)+100;
463+
buf= (char*)palloc(buflen);
464+
snprintf(buf,buflen,
465+
"select modsrc from %s where modname = 'unknown' order by modseq",
466+
pmrelname);
422467

423-
spi_rc=SPI_execute("select modseq, modsrc from pltcl_modules "
424-
"where modname = 'unknown' "
425-
"order by modseq",
426-
false,0);
468+
spi_rc=SPI_execute(buf, false,0);
427469
if (spi_rc!=SPI_OK_SELECT)
428470
elog(ERROR,"select from pltcl_modules failed");
429471

472+
pfree(buf);
473+
430474
/************************************************************
431475
* If there's nothing, module unknown doesn't exist
432476
************************************************************/
433477
if (SPI_processed==0)
434478
{
435-
Tcl_DStringFree(&unknown_src);
436479
SPI_freetuptable(SPI_tuptable);
437480
elog(WARNING,"module \"unknown\" not found in pltcl_modules");
481+
relation_close(pmrel,AccessShareLock);
438482
return;
439483
}
440484

441485
/************************************************************
442-
* There is a module named unknown.Resemble the
486+
* There is a module named unknown.Reassemble the
443487
* source from the modsrc attributes and evaluate
444488
* it in the Tcl interpreter
445489
************************************************************/
446490
fno=SPI_fnumber(SPI_tuptable->tupdesc,"modsrc");
447491

492+
Tcl_DStringInit(&unknown_src);
493+
448494
for (i=0;i<SPI_processed;i++)
449495
{
450496
part=SPI_getvalue(SPI_tuptable->vals[i],
@@ -458,8 +504,19 @@ pltcl_init_load_unknown(Tcl_Interp *interp)
458504
}
459505
}
460506
tcl_rc=Tcl_GlobalEval(interp,Tcl_DStringValue(&unknown_src));
507+
461508
Tcl_DStringFree(&unknown_src);
462509
SPI_freetuptable(SPI_tuptable);
510+
511+
if (tcl_rc!=TCL_OK)
512+
{
513+
UTF_BEGIN;
514+
elog(ERROR,"could not load module \"unknown\": %s",
515+
UTF_U2E(Tcl_GetStringResult(interp)));
516+
UTF_END;
517+
}
518+
519+
relation_close(pmrel,AccessShareLock);
463520
}
464521

465522

@@ -480,11 +537,6 @@ pltcl_call_handler(PG_FUNCTION_ARGS)
480537
FunctionCallInfosave_fcinfo;
481538
pltcl_proc_desc*save_prodesc;
482539

483-
/*
484-
* Initialize interpreters if first time through
485-
*/
486-
pltcl_init_all();
487-
488540
/*
489541
* Ensure that static pointers are saved/restored properly
490542
*/
@@ -558,10 +610,7 @@ pltcl_func_handler(PG_FUNCTION_ARGS)
558610

559611
pltcl_current_prodesc=prodesc;
560612

561-
if (prodesc->lanpltrusted)
562-
interp=pltcl_safe_interp;
563-
else
564-
interp=pltcl_norm_interp;
613+
interp=pltcl_fetch_interp(prodesc->lanpltrusted);
565614

566615
/************************************************************
567616
* Create the tcl command to call the internal
@@ -719,10 +768,7 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS)
719768

720769
pltcl_current_prodesc=prodesc;
721770

722-
if (prodesc->lanpltrusted)
723-
interp=pltcl_safe_interp;
724-
else
725-
interp=pltcl_norm_interp;
771+
interp=pltcl_fetch_interp(prodesc->lanpltrusted);
726772

727773
tupdesc=trigdata->tg_relation->rd_att;
728774

@@ -1152,10 +1198,7 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid)
11521198
prodesc->lanpltrusted=langStruct->lanpltrusted;
11531199
ReleaseSysCache(langTup);
11541200

1155-
if (prodesc->lanpltrusted)
1156-
interp=pltcl_safe_interp;
1157-
else
1158-
interp=pltcl_norm_interp;
1201+
interp=pltcl_fetch_interp(prodesc->lanpltrusted);
11591202

11601203
/************************************************************
11611204
* Get the required information for input conversion of the

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp