XRootD
Loading...
Searching...
No Matches
XrdHttpSecurity.cc
Go to the documentation of this file.
1//------------------------------------------------------------------------------
2// This file is part of XrdHTTP: A pragmatic implementation of the
3// HTTP/WebDAV protocol for the Xrootd framework
4//
5// Copyright (c) 2020 by European Organization for Nuclear Research (CERN)
6// Author: Fabrizio Furano <furano@cern.ch>
7//------------------------------------------------------------------------------
8// XRootD is free software: you can redistribute it and/or modify
9// it under the terms of the GNU Lesser General Public License as published by
10// the Free Software Foundation, either version 3 of the License, or
11// (at your option) any later version.
12//
13// XRootD is distributed in the hope that it will be useful,
14// but WITHOUT ANY WARRANTY; without even the implied warranty of
15// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16// GNU General Public License for more details.
17//
18// You should have received a copy of the GNU Lesser General Public License
19// along with XRootD. If not, see <http://www.gnu.org/licenses/>.
20//------------------------------------------------------------------------------
21
22#include "XrdHttpProtocol.hh"
23#include "XrdHttpTrace.hh"
24#include "XrdHttpSecXtractor.hh"
25#include "Xrd/XrdLink.hh"
32#include "XrdOuc/XrdOucGMap.hh"
33
34namespace XrdHttpProtoInfo
35{
36 extern XrdTlsContext *xrdctx;
37}
38
39XrdOucGMap *XrdHttpProtocol::servGMap = 0; // Grid mapping service
40XrdCryptoFactory *XrdHttpProtocol::myCryptoFactory = 0;
41
42// Static definitions
43#define TRACELINK lp
44
45namespace
46{
47const char *TraceID = "Security";
48}
49
50using namespace XrdHttpProtoInfo;
51
52/******************************************************************************/
53/* I n i t S e c u r i t y */
54/******************************************************************************/
55
56bool XrdHttpProtocol::InitSecurity() {
57#ifdef HAVE_XRDCRYPTO
58 // Borrow the initialization of XrdCryptossl, in order to share the
59 // OpenSSL threading bits
60 if (!(myCryptoFactory = XrdCryptoFactory::GetCryptoFactory("ssl"))) {
61 eDest.Say("Error instantiating crypto factory ssl", "");
62 return false;
63 }
64#endif
65
66// If GRID map file was specified, load the plugin for it
67//
68 if (gridmap) {
69 XrdOucString pars;
70 if (XrdHttpTrace.What & TRACE_DEBUG) pars += "dbg|";
71
72 if (!(servGMap = XrdOucgetGMap(&eDest, gridmap, pars.c_str()))) {
73 eDest.Say("Error loading grid map file:", gridmap);
74 return false;
75 }
76 TRACE(ALL, "using grid map file: "<< gridmap);
77 }
78
79// If a secxtractor was specified, load that too.
80//
81 if (secxtractor)
82 {SSL_CTX *sslctx = (SSL_CTX*)xrdctx->Context(); // Need to avoid this!
83 secxtractor->Init(sslctx, XrdHttpTrace.What);
84 }
85
86// All done
87//
88 return true;
89}
90
91/******************************************************************************/
92/* H a n d l e A u t h e n t i c a t i o n */
93/******************************************************************************/
94
95int
96XrdHttpProtocol::HandleAuthentication(XrdLink* lp)
97{
98 EPNAME("HandleAuthentication");
99 int rc_ssl = SSL_get_verify_result(ssl);
100
101 if (rc_ssl) {
102 TRACEI(DEBUG, " SSL_get_verify_result returned :" << rc_ssl);
103 return 1;
104 }
105
106 XrdTlsPeerCerts pc(SSL_get_peer_certificate(ssl),SSL_get_peer_cert_chain(ssl));
107 XrdCryptoX509Chain chain;
108
109 if ((!pc.hasCert()) ||
110 (myCryptoFactory && !myCryptoFactory->X509ParseStack()(&pc, &chain))) {
111 TRACEI(DEBUG, "No certificate found in peer chain.");
112 chain.Cleanup();
113 return 0;
114 }
115
116 // Extract the DN for the current connection that will be used later on when
117 // handling the gridmap file
118 const char * dn = chain.EECname();
119 const char * eechash = chain.EEChash();
120
121 if (!dn || !eechash) {
122 // X509Chain doesn't assume it owns the underlying certs unless
123 // you explicitly invoke the Cleanup method
124 TRACEI(DEBUG, "Failed to extract DN information.");
125 chain.Cleanup();
126 return 1;
127 }
128
129 if (SecEntity.moninfo) {
130 free(SecEntity.moninfo);
131 }
132
133 SecEntity.moninfo = strdup(dn);
134 TRACEI(DEBUG, " Subject name is : '" << SecEntity.moninfo << "'; hash is " << eechash);
135 // X509Chain doesn't assume it owns the underlying certs unless
136 // you explicitly invoke the Cleanup method
137
138 if (GetVOMSData(lp)) {
139 TRACEI(DEBUG, " No VOMS information for DN: " << SecEntity.moninfo);
140
141 if (isRequiredXtractor) {
142 eDest.Emsg(epname, "Failed extracting required VOMS info for DN: ",
144 chain.Cleanup();
145 return 1;
146 }
147 }
148
149 auto retval = HandleGridMap(lp, eechash);
150 chain.Cleanup();
151 return retval;
152}
153
154
155/******************************************************************************/
156/* H a n d l e G r i d M a p */
157/******************************************************************************/
158
159int
160XrdHttpProtocol::HandleGridMap(XrdLink* lp, const char * eechash)
161{
162 EPNAME("HandleGridMap");
163 char bufname[256];
164
165 if (servGMap) {
166 int mape = servGMap->dn2user(SecEntity.moninfo, bufname, sizeof(bufname), 0);
167 if ( !mape && SecEntity.moninfo[0] ) {
168 TRACEI(DEBUG, " Mapping name: '" << SecEntity.moninfo << "' --> " << bufname);
169 if (SecEntity.name) free(SecEntity.name);
170 SecEntity.name = strdup(bufname);
171 SecEntity.eaAPI->Add("gridmap.name", "1", true);
172 }
173 else {
174 TRACEI(ALL, " Mapping name: " << SecEntity.moninfo << " Failed. err: " << mape);
175
176 if (isRequiredGridmap) {
177 eDest.Emsg(epname, "Required gridmap mapping failed for DN:",
179 return 1;
180 }
181 }
182 }
183
185 TRACEI(DEBUG, " Will fallback name to subject hash: " << eechash);
186 SecEntity.name = strdup(eechash);
187 return 0;
188 }
189
190 if (!SecEntity.name) {
191 // Here we have the user DN, and try to extract an useful user name from it
192 if (SecEntity.name) free(SecEntity.name);
193 SecEntity.name = 0;
194 // To set the name we pick the first CN of the certificate subject
195 // and hope that it makes some sense, it usually does
196 char *lnpos = strstr(SecEntity.moninfo, "/CN=");
197 char bufname2[9];
198
199
200 if (lnpos) {
201 lnpos += 4;
202 char *lnpos2 = index(lnpos, '/');
203 if (lnpos2) {
204 int l = ( lnpos2-lnpos < (int)sizeof(bufname) ? lnpos2-lnpos : (int)sizeof(bufname)-1 );
205 strncpy(bufname, lnpos, l);
206 bufname[l] = '\0';
207
208 // Here we have the string in the buffer. Take the last 8 non-space characters
209 size_t j = 8;
210 strcpy(bufname2, "unknown-"); // note it's 8 chars + '\0' at the end
211 for (int i = (int)strlen(bufname)-1; i >= 0; i--) {
212 if (isalnum(bufname[i])) {
213 j--;
214 bufname2[j] = bufname[i];
215 if (j == 0) break;
216 }
217
218 }
219
220 SecEntity.name = strdup(bufname);
221 TRACEI(DEBUG, " Setting link name: '" << bufname2+j << "'");
222 lp->setID(bufname2+j, 0);
223 }
224 }
225 }
226
227 // If we could not find anything good, take the last 8 non-space characters of the main subject
228 if (!SecEntity.name) {
229 size_t j = 8;
230 SecEntity.name = strdup("unknown-\0"); // note it's 9 chars
231 for (int i = (int)strlen(SecEntity.moninfo)-1; i >= 0; i--) {
232 if (isalnum(SecEntity.moninfo[i])) {
233 j--;
235 if (j == 0) break;
236 }
237 }
238 }
239
240 return 0;
241}
242
243
244/******************************************************************************/
245/* G e t V O M S D a t a */
246/******************************************************************************/
247
248int XrdHttpProtocol::GetVOMSData(XrdLink *lp)
249{
250 TRACEI(DEBUG, " Extracting auth info.");
251
252 // Invoke the Security exctractor plugin which will fill in the XrdSecEntity
253 // with VOMS info, if VOMS is installed. If we have no sec extractor then do
254 // nothing, just plain https will work.
255 if (secxtractor) {
256 // Note: this is kept for compatibility with XrdHttpVOMS which modified the
257 // SecEntity.name filed
258 char *savestr = 0;
259
260 if (servGMap && SecEntity.name) {
261 savestr = strdup(SecEntity.name);
262 }
263
264 int r = secxtractor->GetSecData(lp, SecEntity, ssl);
265
266 if (servGMap && savestr) {
267 if (SecEntity.name) free(SecEntity.name);
268 SecEntity.name = savestr;
269 }
270
271 if (r) {
272 TRACEI(ALL, " Certificate data extraction failed: " << SecEntity.moninfo
273 << " Failed. err: " << r);
274 }
275
276 return r;
277 }
278
279 return 0;
280}
#define DEBUG(x)
#define EPNAME(x)
XrdSysTrace XrdHttpTrace("http")
A pragmatic implementation of the HTTP/DAV protocol for the Xrd framework.
Trace definitions.
XrdOucGMap * XrdOucgetGMap(XrdOucGMapArgs)
Definition XrdOucGMap.cc:92
#define TRACE_DEBUG
Definition XrdTrace.hh:36
#define TRACE(act, x)
Definition XrdTrace.hh:63
#define TRACEI(act, x)
Definition XrdTrace.hh:66
virtual XrdCryptoX509ParseStack_t X509ParseStack()
static XrdCryptoFactory * GetCryptoFactory(const char *factoryname)
void Cleanup(bool keepCA=0)
static char * gridmap
Gridmap file location. The same used by XrdSecGsi.
static XrdSysError eDest
static XrdOucGMap * servGMap
The instance of the DN mapper. Created only when a valid path is given.
static bool compatNameGeneration
static bool isRequiredGridmap
XrdSecEntity SecEntity
Authentication area.
virtual int Init(SSL_CTX *, int)=0
virtual int GetSecData(XrdLink *, XrdSecEntity &, SSL *)=0
virtual int dn2user(const char *dn, char *user, int ulen, time_t now=0)
const char * c_str() const
bool Add(XrdSecAttr &attr)
XrdSecEntityAttr * eaAPI
non-const API to attributes
char * name
Entity's name.
char * moninfo
Information for monitoring.
int Emsg(const char *esfx, int ecode, const char *text1, const char *text2=0)
void Say(const char *text1, const char *text2=0, const char *txt3=0, const char *text4=0, const char *text5=0, const char *txt6=0)
XrdTlsContext * xrdctx