Skip to content

Commit 1faa709

Browse files
authored
Merge pull request from GHSA-v6f3-gh5h-mqwx
[v8.0] Ensure proxy files are always written securely
2 parents 92a5dad + ff6876f commit 1faa709

File tree

7 files changed

+39
-50
lines changed

7 files changed

+39
-50
lines changed

src/DIRAC/Core/Security/ProxyFile.py

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
from DIRAC import S_OK, S_ERROR
88
from DIRAC.Core.Utilities import DErrno
9+
from DIRAC.Core.Utilities.File import secureOpenForWrite
910
from DIRAC.Core.Security.X509Chain import X509Chain # pylint: disable=import-error
1011
from DIRAC.Core.Security.Locations import getProxyLocation
1112

@@ -17,22 +18,11 @@ def writeToProxyFile(proxyContents, fileName=False):
1718
- proxyContents : string object to dump to file
1819
- fileName : filename to dump to
1920
"""
20-
if not fileName:
21-
try:
22-
fd, proxyLocation = tempfile.mkstemp()
23-
os.close(fd)
24-
except OSError:
25-
return S_ERROR(DErrno.ECTMPF)
26-
fileName = proxyLocation
2721
try:
28-
with open(fileName, "w") as fd:
22+
with secureOpenForWrite(fileName) as fd:
2923
fd.write(proxyContents)
3024
except Exception as e:
3125
return S_ERROR(DErrno.EWF, f" {fileName}: {repr(e).replace(',)', ')')}")
32-
try:
33-
os.chmod(fileName, stat.S_IRUSR | stat.S_IWUSR)
34-
except Exception as e:
35-
return S_ERROR(DErrno.ESPF, f"{fileName}: {repr(e).replace(',)', ')')}")
3626
return S_OK(fileName)
3727

3828

src/DIRAC/Core/Security/m2crypto/X509CRL.py

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
11
""" X509CRL is a class for managing X509CRL
22
This class is used to manage the revoked certificates....
33
"""
4-
import stat
5-
import os
6-
import tempfile
74
import re
85
import datetime
96

107
import M2Crypto
118
from DIRAC import S_OK, S_ERROR
129
from DIRAC.Core.Utilities import DErrno
10+
from DIRAC.Core.Utilities.File import secureOpenForWrite
1311

1412
# pylint: disable=broad-except
1513

@@ -72,17 +70,10 @@ def dumpAllToFile(self, filename=False):
7270
if not self.__loadedCert:
7371
return S_ERROR("No certificate loaded")
7472
try:
75-
if not filename:
76-
fd, filename = tempfile.mkstemp()
77-
os.close(fd)
78-
with open(filename, "w", encoding="ascii") as fd:
73+
with secureOpenForWrite(filename) as fd:
7974
fd.write(self.__pemData)
8075
except Exception as e:
8176
return S_ERROR(DErrno.EWF, f"{filename}: {repr(e).replace(',)', ')')}")
82-
try:
83-
os.chmod(filename, stat.S_IRUSR | stat.S_IWUSR)
84-
except Exception as e:
85-
return S_ERROR(DErrno.ESPF, f"{filename}: {repr(e).replace(',)', ')')}")
8677
return S_OK(filename)
8778

8879
def hasExpired(self):

src/DIRAC/Core/Security/m2crypto/X509Chain.py

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,6 @@
88
99
"""
1010
import copy
11-
import os
12-
import stat
13-
import tempfile
1411
import hashlib
1512

1613
import re
@@ -21,6 +18,7 @@
2118
from DIRAC import S_OK, S_ERROR
2219
from DIRAC.Core.Utilities import DErrno
2320
from DIRAC.Core.Utilities.Decorators import executeOnlyIf, deprecated
21+
from DIRAC.Core.Utilities.File import secureOpenForWrite
2422
from DIRAC.ConfigurationSystem.Client.Helpers import Registry
2523
from DIRAC.Core.Security.m2crypto import PROXY_OID, LIMITED_PROXY_OID, DIRAC_GROUP_OID, DEFAULT_PROXY_STRENGTH
2624
from DIRAC.Core.Security.m2crypto.X509Certificate import X509Certificate
@@ -492,14 +490,10 @@ def generateProxyToFile(self, filePath, lifetime, diracGroup=False, strength=DEF
492490
if not retVal["OK"]:
493491
return retVal
494492
try:
495-
with open(filePath, "w") as fd:
493+
with secureOpenForWrite(filePath) as fd:
496494
fd.write(retVal["Value"])
497495
except Exception as e:
498496
return S_ERROR(DErrno.EWF, f"{filePath} :{repr(e).replace(',)', ')')}")
499-
try:
500-
os.chmod(filePath, stat.S_IRUSR | stat.S_IWUSR)
501-
except Exception as e:
502-
return S_ERROR(DErrno.ESPF, f"{filePath} :{repr(e).replace(',)', ')')}")
503497
return S_OK()
504498

505499
@needCertList
@@ -880,17 +874,10 @@ def dumpAllToFile(self, filename=False):
880874
return retVal
881875
pemData = retVal["Value"]
882876
try:
883-
if not filename:
884-
fd, filename = tempfile.mkstemp()
885-
os.close(fd)
886-
with open(filename, "w") as fp:
887-
fp.write(pemData)
877+
with secureOpenForWrite(filename) as fh:
878+
fh.write(pemData)
888879
except Exception as e:
889880
return S_ERROR(DErrno.EWF, f"{filename} :{repr(e).replace(',)', ')')}")
890-
try:
891-
os.chmod(filename, stat.S_IRUSR | stat.S_IWUSR)
892-
except Exception as e:
893-
return S_ERROR(DErrno.ESPF, f"{filename} :{repr(e).replace(',)', ')')}")
894881
return S_OK(filename)
895882

896883
@needCertList

src/DIRAC/Core/Utilities/File.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111
import sys
1212
import re
1313
import errno
14+
import stat
15+
import tempfile
16+
from contextlib import contextmanager
1417

1518
# Translation table of a given unit to Bytes
1619
# I know, it should be kB...
@@ -253,6 +256,27 @@ def convertSizeUnits(size, srcUnit, dstUnit):
253256
return -sys.maxsize
254257

255258

259+
@contextmanager
260+
def secureOpenForWrite(filename=None, *, text=True):
261+
"""Securely open a file for writing.
262+
263+
If filename is not provided, a file is created in tempfile.gettempdir().
264+
The file always created with mode 600.
265+
266+
:param string filename: name of file to be opened
267+
"""
268+
if filename:
269+
fd = os.open(
270+
path=filename,
271+
flags=os.O_WRONLY | os.O_CREAT | os.O_TRUNC,
272+
mode=stat.S_IRUSR | stat.S_IWUSR,
273+
)
274+
else:
275+
fd, filename = tempfile.mkstemp(text=text)
276+
with open(fd, "w" if text else "wb", encoding="ascii") as fd:
277+
yield fd
278+
279+
256280
if __name__ == "__main__":
257281
for p in sys.argv[1:]:
258282
print(f"{p} : {getGlobbedTotalSize(p)} bytes")

src/DIRAC/FrameworkSystem/private/authorization/utils/Tokens.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import os
22
import re
33
import jwt
4-
import stat
54
import time
65
import json
76
import datetime
87

98
from DIRAC import S_OK, S_ERROR
109
from DIRAC.Core.Utilities import DErrno
10+
from DIRAC.Core.Utilities.File import secureOpenForWrite
1111
from DIRAC.ConfigurationSystem.Client.Helpers import Registry
1212
from DIRAC.Resources.IdProvider.IdProviderFactory import IdProviderFactory
1313

@@ -83,14 +83,10 @@ def writeToTokenFile(tokenContents, fileName):
8383
"""
8484
location = getTokenFileLocation(fileName)
8585
try:
86-
with open(location, "w") as fd:
86+
with secureOpenForWrite(location) as fd:
8787
fd.write(tokenContents)
8888
except Exception as e:
8989
return S_ERROR(DErrno.EWF, f" {location}: {repr(e)}")
90-
try:
91-
os.chmod(location, stat.S_IRUSR | stat.S_IWUSR)
92-
except Exception as e:
93-
return S_ERROR(DErrno.ESPF, f"{location}: {repr(e)}")
9490
return S_OK(location)
9591

9692

src/DIRAC/Interfaces/Utilities/DConfigCache.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
#!/usr/bin/env python
22
import os
33
import re
4-
import stat
54
import time
65
import pickle
76
import tempfile
87

98
from DIRAC.Core.Base.Script import Script
9+
from DIRAC.Core.Utilities.File import secureOpenForWrite
1010
from DIRAC.ConfigurationSystem.Client.ConfigurationData import gConfigurationData
1111

1212

@@ -67,8 +67,7 @@ def cacheConfig(self):
6767
if self.newConfig:
6868
self.__cleanCacheDirectory()
6969

70-
with open(self.configCacheName, "wb") as fcache:
71-
os.chmod(self.configCacheName, stat.S_IRUSR | stat.S_IWUSR)
70+
with secureOpenForWrite(self.configCacheName, text=False) as fcache:
7271
pickle.dump(gConfigurationData.mergedCFG, fcache)
7372
else:
7473
try:

src/DIRAC/WorkloadManagementSystem/Utilities/PilotWrapper.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
from __future__ import print_function
3535
3636
import os
37+
import io
3738
import stat
3839
import tempfile
3940
import sys
@@ -130,7 +131,8 @@ def pilotWrapperScript(
130131
for pfName, encodedPf in pilotFilesCompressedEncodedDict.items():
131132
compressedString += """
132133
try:
133-
with open('%(pfName)s', 'wb') as fd:
134+
fd = os.open('%(pfName)s', os.O_WRONLY | os.O_CREAT | os.O_TRUNC, stat.S_IRUSR | stat.S_IWUSR)
135+
with io.open(fd, 'wb') as fd:
134136
if sys.version_info < (3,):
135137
fd.write(bz2.decompress(base64.b64decode(\"\"\"%(encodedPf)s\"\"\")))
136138
else:

0 commit comments

Comments
 (0)