XRootD
Loading...
Searching...
No Matches
XrdVomsFun.cc
Go to the documentation of this file.
1/******************************************************************************/
2/* */
3/* X r d V o m s F u n . c c */
4/* */
5/* (C) 2013 G. Ganis, CERN */
6/* */
7/* All rights reserved. The copyright holder's institutional names may not */
8/* be used to endorse or promote products derived from this software without */
9/* specific prior written permission. */
10/* */
11/* This file is part of the VOMS extraction XRootD plug-in software suite, */
12/* here after called VOMS-XRootD (see https://github.com/gganis/voms). */
13/* */
14/* VOMS-XRootD is free software: you can redistribute it and/or modify it */
15/* under the terms of the GNU Lesser General Public License as published by */
16/* the Free Software Foundation, either version 3 of the License, or (at */
17/* your option) any later version. */
18/* */
19/* VOMS-XRootD is distributed in the hope that it will be useful, but */
20/* WITHOUT ANY WARRANTY, not even the implied warranty of MERCHANTABILITY or */
21/* FITNESS FOR A PARTICULAR PURPOSE. */
22/* See the GNU Lesser General Public License for more details. */
23/* */
24/* You should have received a copy of the GNU Lesser General Public License */
25/* along with VOMS-XRootD in a file called COPYING.LGPL (LGPL license) and */
26/* file COPYING (GPL license). If not, see <http://www.gnu.org/licenses/>. */
27/* */
28/******************************************************************************/
29
30/******************************************************************************/
31/* */
32/* See README.md for hints about usage of this library */
33/* */
34/******************************************************************************/
35
36#include <cstdio>
37#include <cstdlib>
38#include <cstring>
39#include <cerrno>
40
41#include "XrdVoms.hh"
42#include "XrdVomsFun.hh"
43#include "XrdVomsTrace.hh"
44#include "XrdVomsMapfile.hh"
45
46#ifdef HAVE_XRDCRYPTO
49#endif
52
53/******************************************************************************/
54/* L o c a l D e f i n e s */
55/******************************************************************************/
56
57#ifndef SafeFree
58#define SafeFree(x) { if (x) free(x) ; x = 0; }
59#endif
60
61#define VOMSDBG(m) \
62 if (gDebug) { \
63 PRINT(m); \
64 }
65
66#define VOMSDBGSUBJ(m, c) \
67 if (gDebug) { \
68 XrdOucString subject; \
69 NameOneLine(X509_get_subject_name(c), subject); \
70 PRINT(m << subject); \
71 }
72
73#define VOMSREPLACE(a, f, e) \
74 if (a.length() > 0) { \
75 f.replace("<g>", e.grps); \
76 f.replace("<r>", e.role); \
77 f.replace("<vo>", e.vorg); \
78 f.replace("<an>", e.endorsements); \
79 }
80
81#define VOMSSPTTAB(a) \
82 if (a.length() > 0) { \
83 int sp = -1; \
84 while ((sp = a.find(' ', sp+1)) != STR_NPOS) { a[sp] = '\t'; } \
85 }
86
87#define FATAL(x) {std::cerr <<"VomsFun: "<<x<<std::endl; aOK = false;}
88
89namespace
90{
91static const int gSelAll = 0;
92static const int gSelGrps = 1;
93static const short gUseFirst = 0;
94static const short gUseLast = 1;
95static const short gUseAll = 2;
96}
97
98/******************************************************************************/
99/* C o n s t r u c t o r */
100/******************************************************************************/
101
103 : gGrpWhich(gUseAll), gDebug(0), gDest(erp),
104 gLogger(erp.logger())
105{
106#ifdef HAVE_XRDCRYPTO
107 gCertFmt = gCertRaw; // certfmt:raw|pem|x509 [raw]
108#else
109 gCertFmt = gCertPEM; // certfmt:pem|x509 [pem]
110#endif
111};
112
113/******************************************************************************/
114/* N a m e O n e L i n e */
115/******************************************************************************/
116
117// Function to convert X509_NAME into a one-line human readable string
118//
119void XrdVomsFun::NameOneLine(X509_NAME *nm, XrdOucString &s)
120{
121 BIO *mbio = BIO_new(BIO_s_mem());
122 X509_NAME_print_ex(mbio, nm, 0, XN_FLAG_COMPAT);
123 char *data = 0;
124 long len = BIO_get_mem_data(mbio, &data);
125 s = "/";
126 s.insert(data, 1, len);
127 BIO_free(mbio);
128 s.replace(", ", "/");
129
130 // Done
131 return;
132}
133
134/******************************************************************************/
135/* F m t R e p l a c e */
136/******************************************************************************/
137
138// Method to convert X509_NAME into a one-line human readable string
139//
140void XrdVomsFun::FmtReplace(XrdSecEntity &ent)
141{
142 XrdOucString gf(gGrpFmt), rf(gRoleFmt), vf(gVoFmt);
143
144 VOMSREPLACE(gGrpFmt, gf, ent);
145 VOMSREPLACE(gRoleFmt, rf, ent);
146 VOMSREPLACE(gVoFmt, vf, ent);
147
148 if (gf.length() > 0) {
149 SafeFree(ent.grps);
150 ent.grps = strdup(gf.c_str());
151 }
152 if (rf.length() > 0) {
153 SafeFree(ent.role);
154 ent.role = strdup(rf.c_str());
155 }
156 if (vf.length() > 0) {
157 SafeFree(ent.vorg);
158 ent.vorg = strdup(vf.c_str());
159 }
160}
161
162/******************************************************************************/
163/* F m t E x t r a c t */
164/******************************************************************************/
165
166// Method to extract out a tag
167//
168void XrdVomsFun::FmtExtract(XrdOucString &out,
169 XrdOucString in, const char *tag)
170{
171 // Output group format string
172 int igf = in.find(tag);
173 if (igf != STR_NPOS) {
174 int from = igf + strlen(tag);
175 if (in[from] == '"') {
176 out.assign(in, from + 1);
177 out.erase(out.find('"'));
178 } else {
179 out.assign(in, from);
180 while(out.endswith(' ')) out.erasefromend(1);
181 }
182 }
183}
184
185/******************************************************************************/
186/* V O M S F u n */
187/******************************************************************************/
188
189// The Main Method
190//
192{
193 // Implementation of XrdSecgsiAuthzFun extracting the information from the
194 // proxy chain in entity.creds
195 EPNAME("Fun");
196
197 vomsdata v;
198 X509 *pxy = 0;
199 STACK_OF(X509) *stk = 0;
200 int freestk = 1;
201
202// Set extractor name in the XrdSecEntity object
203//
204 strcpy(ent.prox, "xrdvoms");
205
206 if (gCertFmt == gCertRaw) {
207#ifdef HAVE_XRDCRYPTO
208 //
209 // RAW format
210 //
212 if (!c) {
213 PRINT("ERROR: no proxy chain found!");
214 return -1;
215 }
216
217 XrdCryptoX509 *xp = c->End();
218 if (!xp) {
219 PRINT("ERROR: no proxy certificate in chain!");
220 return -1;
221 }
222 pxy = (X509 *) xp->Opaque();
223 VOMSDBGSUBJ("proxy: ", pxy)
224 freestk = 2;
225
226 stk =sk_X509_new_null();
227 XrdCryptoX509 *xxp = c->Begin();
228 while (xxp) {
229 if (xxp == c->End()) break;
230 if (xxp->type != XrdCryptoX509::kCA) {
231 VOMSDBGSUBJ("adding cert: ", (X509 *) xxp->Opaque())
232 sk_X509_push(stk, (X509 *) xxp->Opaque());
233 }
234 xxp = c->Next();
235 }
236#else
237 //
238 // Do not have support for RAW format
239 //
240 PRINT("ERROR: compiled without support for RAW format! Re-run with 'certfmt=pem'");
241 return -1;
242#endif
243 } else if (gCertFmt == gCertPEM) {
244 //
245 // PEM format
246 //
247 // Create a bio_mem to store the certificates
248 BIO *bmem = BIO_new(BIO_s_mem());
249 if (!bmem) {
250 PRINT("unable to create BIO for memory operations");
251 return -1;
252 }
253
254 // Write data to BIO
255 int nw = BIO_write(bmem, (const void *)(ent.creds), ent.credslen);
256 if (nw != ent.credslen) {
257 PRINT("problems writing data to memory BIO (nw: "<<nw<<")");
258 BIO_free(bmem);
259 return -1;
260 }
261
262 // Get certificate from BIO
263 if (!(pxy = PEM_read_bio_X509(bmem,0,0,0))) {
264 PRINT("unable to read certificate to memory BIO");
265 BIO_free(bmem);
266 return -1;
267 }
268 VOMSDBGSUBJ("proxy: ", pxy)
269 //
270 // The chain now
271 X509 *xc = 0;
272 stk =sk_X509_new_null();
273 while ((xc = PEM_read_bio_X509(bmem,0,0,0))) {
274 VOMSDBGSUBJ("adding cert: ", xc)
275 sk_X509_push(stk, xc);
276 }
277 //
278 // Free BIO
279 BIO_free(bmem);
280
281 } else {
282 //
283 // STACK_OF(X509) format
284 //
285 Voms_x509_in_t *voms_in = (Voms_x509_in_t *) ent.creds;
286 pxy = voms_in->cert;
287 stk = voms_in->chain;
288 freestk = 0;
289 }
290
291 bool extfound = 0;
292 XrdOucString endor, grps, role, vo, xendor, xgrps, xrole, xvo;
293 if (v.Retrieve(pxy, stk, RECURSE_CHAIN)) {
294 VOMSDBG("retrieval successful");
295 extfound = 1;
296 std::vector<voms>::iterator i = v.data.begin();
297 for ( ; i != v.data.end(); i++) {
298 VOMSDBG("found VO: " << (*i).voname);
299 xvo = (*i).voname.c_str(); VOMSSPTTAB(xvo);
300 // Filter the VO? (*i) is voms
301 if (gVOs.Num() > 0 && !gVOs.Find((*i).voname.c_str())) continue;
302 // Save VO name (in tuple mode this is done later, in the loop over groups)
303 if (gGrpWhich < gUseAll) vo = xvo;
304 std::vector<data> dat = (*i).std;
305 std::vector<data>::iterator idat = dat.begin();
306 // Same size as std::vector<data> by construction (same information in compact form)
307 std::vector<std::string> fqa = (*i).fqan;
308 std::vector<std::string>::iterator ifqa = fqa.begin();
309 for (; idat != dat.end(); idat++, ifqa++) {
310 VOMSDBG(" ---> group: '"<<(*idat).group<<"', role: '"<<(*idat).role<<"', cap: '" <<(*idat).cap<<"'");
311 VOMSDBG(" ---> fqan: '"<<(*ifqa)<<"'");
312 xgrps = (*idat).group.c_str(); VOMSSPTTAB(xgrps);
313 xrole = (*idat).role.c_str(); VOMSSPTTAB(xrole);
314 xendor = (*ifqa).c_str(); VOMSSPTTAB(xendor);
315 bool fillgrp = true;
316 if (gGrps.Num() && !gGrps.Find((*idat).group.c_str()))
317 fillgrp = false;
318 if (fillgrp) {
319 if (gGrpWhich == gUseAll) {
320 if (vo.length() > 0) vo += " ";
321 vo += (*i).voname.c_str();
322 if (grps.length() > 0) grps += " ";
323 grps += (*idat).group.c_str();
324 if (role.length() > 0) role += " ";
325 role += (*idat).role.c_str();
326 if (endor.length() > 0) endor += ",";
327 endor += (*ifqa).c_str();
328 } else {
329 grps = (*idat).group.c_str();
330 role = (*idat).role.c_str();
331 endor = (*ifqa).c_str();
332 }
333 }
334 // If we are asked to take the first we break
335 if (gGrpWhich == gUseFirst && grps.length() > 0) break;
336 }
337 if (grps.length() <= 0) {
338 // Reset all the fields
339 role = "";
340 vo = "";
341 endor = "";
342 }
343 }
344 // Save the information found
345 SafeFree(ent.vorg);
346 SafeFree(ent.grps);
347 SafeFree(ent.role);
349 if (vo.length() > 0) {
350 ent.vorg = strdup(vo.c_str());
351 // Save the groups
352 if (grps.length() > 0) ent.grps = strdup(grps.c_str());
353 if (role.length() > 0) ent.role = strdup(role.c_str());
354 // Save the whole string in endorsements
355 if (endor.length() > 0) ent.endorsements = strdup(endor.c_str());
356 } else if (extfound) {
357 VOMSDBG("VOMS extensions do not match required criteria ("<<gRequire<<")");
358 }
359 } else {
360 PRINT("retrieval FAILED: "<< v.ErrorMessage());
361 }
362
363 // Fix spaces in XrdSecEntity::name
364// char *sp = 0;
365// while ((sp = strchr(ent.name, ' '))) { *sp = '\t'; }
366
367 // Adjust the output format, if required
368 FmtReplace(ent);
369
370 // Free memory taken by the chain, if required
371 if (stk && freestk > 0) {
372 if (freestk == 1) {
373 sk_X509_pop_free(stk, X509_free);
374 X509_free(pxy);
375 } else if (freestk == 2) {
376 while (sk_X509_pop(stk)) { }
377 sk_X509_free(stk);
378 }
379 }
380
381 // Success or failure?
382 int rc = !ent.vorg ? -1 : 0;
383 if (rc == 0 && gGrps.Num() && !ent.grps) rc = -1;
384
385 // If we have a mapfile object, apply the mapping now.
386 if (m_mapfile) {
387 auto mapfile_rc = m_mapfile->Apply(ent);
388 rc = rc ? rc : mapfile_rc;
389 }
390
391 // Done
392 return rc;
393}
394
395/******************************************************************************/
396/* V O M S I n i t */
397/******************************************************************************/
398
399// Method to initialize this object
400//
401int XrdVomsFun::VOMSInit(const char *cfg)
402{
403 // Initialize the relevant parameters from the 'cfg' string.
404 // Return -1 on failure.
405 // Otherwise, the return code indicates the format required by the main function
406 // defined by 'certfmt' below.
407 //
408 // Supported options:
409 // certfmt=raw|pem|x509 Certificate format: [raw]
410 // raw to be used with XrdCrypto tools
411 // pem PEM base64 format (i.e. cert files)
412 // x509 As a STACK_OF(X509)
413 // grpopt=opt What to do with the group names: [1]
414 // opt = sel * 10 + which
415 // with 'sel'
416 // 0 consider all those present
417 // 1 select among those specified by
418 // 'grps' (see below)
419 // and 'which'
420 // 0 take the first one
421 // 1 take the last
422 // 2 take all
423 //
424 // grpopt=useall|usefirst|uselast
425 // useall: all applicable groups
426 // usefirst: only the first applicable on
427 // uselast: only the last applicable on
428 //
429 // grps=grp1[,grp2,...] Group(s) for which the information is
430 // extracted; if specified the gropt
431 // 'sel' is set to 1 regardless of
432 // the setting.
433 // vos=vo1[,vo2,...] VOs to be considered; the first match is taken
434 // grpfmt=<string> Format to use for XrdSecEntity::grps
435 // rolefmt=<string> Format to use for XrdSecEntity::role
436 // vofmt=<string> Format to use for XrdSecEntity::vorg
437 // Recognized place holders in the above
438 // format strings:
439 // <r> role, from the parsing procedure
440 // <g> group
441 // <vo> VO
442 // <an> Full Qualified Attribute Name
443 // For example, rolefmt=<g>,grpfmt=<r> will
444 // inverse the group and role in XrdSecEntity
445 // dbg To force verbose mode
446 //
447 EPNAME("Init");
448 vomsdata vomsInit; // This forces libssl initialization at load time
449
450 XrdOucString oos(cfg);
451
452 XrdOucString fmt, go, grps, voss, gfmt, rfmt, vfmt, sdbg, sdbg2;
453 XrdOucString gr, vo, ss;
454 bool aOK = true;
455
456 if (oos.length() > 0) {
457
458#define NTAG 9
459 XrdOucString *var[NTAG] = { &fmt, &go, &grps, &voss, &gfmt, &rfmt, &vfmt,
460 &sdbg, &sdbg2};
461 const char *tag[] = {"certfmt=", "grpopt=", "grps=", "vos=",
462 "grpfmt=", "rolefmt=", "vofmt=", "dbg", "dbg2"};
463 int jb[NTAG], je[NTAG];
464
465 // Begin of ranges
466 int i = 0, j = -1;
467 for(; i < NTAG; i++) {
468 jb[i] = -1;
469 int j = oos.find(tag[i]);
470 if (j != STR_NPOS) jb[i] = j;
471// DEBUG("["<<i<<"] "<<tag[i]<<" is "<<(j == STR_NPOS?"no":"")<<"spec");
472 }
473 // End of ranges
474 for(i = 0; i < NTAG; i++) {
475 je[i] = -1;
476// DEBUG("-------------");
477 if (jb[i] > -1) {
478 int k = -1;
479 for(j = 0; j < NTAG; j++) {
480 if (j != i) {
481 if (jb[j] > jb[i] && (k < 0 || jb[j] < jb[k])) k = j;
482// DEBUG("jb[" << j << "] = " << jb[j] <<" jb[ "<< i<<"] = "<<jb[i] << " -> k:" << k);
483 }
484 }
485 if (k >= 0) {
486 je[i] = jb[k] - 2;
487 } else {
488 je[i] = oos.length() - 1;
489 }
490 if (i < NTAG-2) {
491 ss.assign(oos, jb[i], je[i]);
492 FmtExtract(*var[i], ss, tag[i]);
493 } else {
494 *var[i] = tag[i];
495 }
496 DEBUG(tag[i] <<"\"" << *var[i] << "\"");
497 }
498// DEBUG("jb["<<i<<"] = "<<jb[i] <<" ---> "<< "je["<<i<<"] = "<<je[i]);
499 }
500
501
502 // Certificate format
503 if (fmt.length() > 0) {
504 if (fmt == "raw") {
505#ifdef HAVE_XRDCRYPTO
506 gCertFmt = gCertRaw;
507#else
508 //
509 // Do not have support for RAW format
510 //
511 PRINT("VomsFun: support for RAW format not available: forcing PEM");
512 gCertFmt = gCertPEM;
513#endif
514 } else if (fmt == "pem") {
515 gCertFmt = gCertPEM;
516 } else if (fmt == "x509") {
517 gCertFmt = gCertX509;
518 }
519 else FATAL("Unsupported cert format - '"<<fmt.c_str()<<"'.")
520 }
521
522 // Group option
523 if (go.length() > 0) {
524 if (go.isdigit()) {
525 int grpopt = go.atoi();
526 int n = grpopt / 10;
527 if (n != gSelAll && n != gSelGrps) {
528 FATAL("grpopt 'select' must be in [0,1] not '"<<n<<"'");
529 }
530 gGrpWhich = grpopt % 10;
531 if (gGrpWhich != gUseFirst && gGrpWhich != gUseLast
532 && gGrpWhich != gUseAll) {
533 FATAL("grpopt 'which' must be in [0,2] not '"<<gGrpWhich<<"'");
534 }
535 } else {
536 if (go == "useall") gGrpWhich = gUseAll;
537 else if (go == "usefirst") gGrpWhich = gUseFirst;
538 else if (go == "uselast") gGrpWhich = gUseLast;
539 else FATAL("Invalid grpopt '"<<go<<"'");
540 }
541 gRequire = "grpopt="; gRequire += go;
542 }
543
544 // Groups selection
545 if (grps.length() > 0) {
546 int from = 0, flag = 1;
547 while ((from = grps.tokenize(gr, from, ',')) != -1) {
548 // Analyse tok
549 VOMSSPTTAB(gr);
550 gGrps.Add(gr.c_str(), &flag);
551 }
552 if (gRequire.length() > 0) gRequire += ";";
553 gRequire += "grps="; gRequire += grps;
554 }
555
556 // VO selection
557 if (voss.length() > 0) {
558 int from = 0, flag = 1;
559 while ((from = voss.tokenize(vo, from, ',')) != -1) {
560 // Analyse tok
561 VOMSSPTTAB(vo);
562 gVOs.Add(vo.c_str(), &flag);
563 }
564 if (gRequire.length() > 0) gRequire += ";";
565 gRequire += "vos="; gRequire += voss;
566 }
567
568 // Output group format string
569 FmtExtract(gGrpFmt, gfmt, "grpfmt=");
570 // Output role format string
571 FmtExtract(gRoleFmt, rfmt, "rolefmt=");
572 // Output vo format string
573 FmtExtract(gVoFmt, vfmt, "vofmt=");
574
575 // Verbose mode
576 if (sdbg == "dbg" && !gDebug) gDebug = 1;
577 if (sdbg2 == "dbg2") gDebug = 2;
578 }
579
580 // Notify
581 const char *cfmt[3] = { "raw", "pem base64", "STACK_OF(X509)" };
582 const char *cgrs[2] = { "all groups", "specified group(s)"};
583 const char *cgrw[3] = { "first", "last", "all" };
584 int n = (gGrps.Num() ? 1 : 0);
585 PRINT("++++++++++++++++++ VOMS plug-in +++++++++++++++++++++++++++++++");
586 PRINT("+++ proxy fmt: "<< cfmt[gCertFmt]);
587 PRINT("+++ group option: "<<cgrw[gGrpWhich]<<" of "<<cgrs[n]);
588 if (grps.length() > 0) {
589 PRINT("+++ group(s): "<< grps);
590 } else {
591 PRINT("+++ group(s): <not specified>");
592 }
593 if (gGrpFmt.length() > 0)
594 PRINT("+++ grps fmt: "<< gGrpFmt);
595 if (gRoleFmt.length() > 0)
596 PRINT("+++ role fmt: "<< gRoleFmt);
597 if (gVoFmt.length() > 0)
598 PRINT("+++ vorg fmt: "<< gVoFmt);
599 if (gVOs.Num() > 0) {PRINT("+++ VO(s): "<< voss);}
600 else {PRINT("+++ VO(s): all");}
601 PRINT("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
602
603 m_mapfile = XrdVomsMapfile::Configure(&gDest);
604 if (m_mapfile == VOMS_MAP_FAILED) {
605 aOK = false;
606 PRINT("VOMS mapfile requested but initialization failed; failing VOMS plugin config.");
607 }
608
609 // Done
610 return (aOK ? gCertFmt : -1);
611}
#define DEBUG(x)
#define EPNAME(x)
#define PRINT(y)
#define STR_NPOS
#define SafeFree(x)
#define FATAL(msg)
#define VOMSSPTTAB(a)
Definition XrdVomsFun.cc:81
#define NTAG
#define VOMSREPLACE(a, f, e)
Definition XrdVomsFun.cc:73
#define VOMSDBG(m)
Definition XrdVomsFun.cc:61
#define VOMSDBGSUBJ(m, c)
Definition XrdVomsFun.cc:66
#define VOMS_MAP_FAILED
XrdCryptoX509 * Next()
XrdCryptoX509 * Begin()
XrdCryptoX509 * End() const
virtual XrdCryptoX509data Opaque()
T * Add(const char *KeyVal, T *KeyData, const int LifeTime=0, XrdOucHash_Options opt=Hash_default)
T * Find(const char *KeyVal, time_t *KeyTime=0)
void insert(const int i, int start=-1)
void assign(const char *s, int j, int k=-1)
int erasefromend(int sz=0)
bool endswith(char c)
int erase(int start=0, int size=0)
int replace(const char *s1, const char *s2, int from=0, int to=-1)
int find(const char c, int start=0, bool forward=1)
int length() const
bool isdigit(int from=0, int to=-1)
long atoi(int from=0, int to=-1)
int tokenize(XrdOucString &tok, int from, char del=':')
const char * c_str() const
char * vorg
Entity's virtual organization(s)
int credslen
Length of the 'creds' data.
char prox[XrdSecPROTOIDSIZE]
Auth extractor used (e.g. xrdvoms)
char * creds
Raw entity credentials or cert.
char * grps
Entity's group name(s)
char * role
Entity's role(s)
char * endorsements
Protocol specific endorsements.
XrdVomsFun(XrdSysError &erp)
int VOMSInit(const char *cfg)
int VOMSFun(XrdSecEntity &ent)
static XrdVomsMapfile * Configure(XrdSysError *)
int Apply(XrdSecEntity &)
X509 * cert
Definition XrdVoms.hh:40