XRootD
XrdSecsssAdmin.cc
Go to the documentation of this file.
1 /******************************************************************************/
2 /* */
3 /* X r d S e c s s s A d m i n . c c */
4 /* */
5 /* (c) 2008 by the Board of Trustees of the Leland Stanford, Jr., University */
6 /* All Rights Reserved */
7 /* Produced by Andrew Hanushevsky for Stanford University under contract */
8 /* DE-AC02-76-SFO0515 with the Department of Energy */
9 /* */
10 /* This file is part of the XRootD software suite. */
11 /* */
12 /* XRootD is free software: you can redistribute it and/or modify it under */
13 /* the terms of the GNU Lesser General Public License as published by the */
14 /* Free Software Foundation, either version 3 of the License, or (at your */
15 /* option) any later version. */
16 /* */
17 /* XRootD is distributed in the hope that it will be useful, but WITHOUT */
18 /* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */
19 /* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public */
20 /* License for more details. */
21 /* */
22 /* You should have received a copy of the GNU Lesser General Public License */
23 /* along with XRootD in a file called COPYING.LESSER (LGPL license) and file */
24 /* COPYING (GPL license). If not, see <http://www.gnu.org/licenses/>. */
25 /* */
26 /* The copyright holder's institutional names and contributor's names may not */
27 /* be used to endorse or promote products derived from this software without */
28 /* specific prior written permission of the institution or contributor. */
29 /******************************************************************************/
30 
31 #include <cctype>
32 #include <iostream>
33 #include <limits.h>
34 #include <cstdlib>
35 #include <cstdio>
36 #include <fcntl.h>
37 #include <ctime>
38 #include <sys/param.h>
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 
42 #include "XrdOuc/XrdOucErrInfo.hh"
43 #include "XrdSys/XrdSysE2T.hh"
44 #include "XrdSys/XrdSysPlatform.hh"
45 #include "XrdSys/XrdSysTimer.hh"
46 
47 #include "XrdSecsss/XrdSecsssKT.hh"
48 
49 /******************************************************************************/
50 /* D e f i n e s */
51 /******************************************************************************/
52 
53 #define eMsg(x) std::cerr <<XrdpgmName <<": " <<x << std::endl
54 
57  const char *Action;
58  const char *KeyName;
59  const char *KeyUser;
60  const char *KeyGrup;
61  const char *KeyFile;
62  time_t Expdt;
63  int Debug;
64  int Keep;
65  int KeyLen;
66  int KeyNum;
67  char Sort;
68 
70  KeyGrup(0), KeyFile(0),
71  Expdt(0), Debug(0), Keep(3), KeyLen(32),
72  KeyNum(-1), Sort('k') {}
74 };
75 
76 /******************************************************************************/
77 /* G l o b a l s */
78 /******************************************************************************/
79 
80 static const char *XrdpgmName;
81 
82 /******************************************************************************/
83 /* m a i n */
84 /******************************************************************************/
85 
86 int main(int argc, char **argv)
87 {
88  extern char *optarg;
89  extern int optopt, optind, opterr;
94  extern time_t getXDate(const char *cDate);
95  extern void Usage(int rc, const char *opn=0, const char *opv=0);
96 
98  enum What2Do {doAdd, doInst, doDel, doList};
99  char c, *sp;
100  const char *validOpts = "dg:h:k:l:n:s:u:x:";
101  int rc;
102  What2Do doIt = doList;
103 
104 // Get the name of our program
105 //
106  XrdpgmName = ((sp = rindex(argv[0], '/')) ? sp+1 : argv[0]);
107 
108 // Process the options
109 //
110  opterr = 0;
111  if (argc > 1 && '-' == *argv[1])
112  while ((c = getopt(argc,argv,validOpts))
113  && ((unsigned char)c != 0xff))
114  { switch(c)
115  {
116  case 'd': Opt.Debug = 1;
117  break;
118  case 'g': Opt.KeyGrup = optarg;
119  break;
120  case 'h': if ((Opt.Keep = atoi(optarg)) <= 0) Usage(1, "-s", optarg);
121  break;
122  case 'k': Opt.KeyName = optarg;
123  break;
124  case 'l': if ((Opt.KeyLen = atoi(optarg)) <= 0
126  Usage(1, "-l", optarg);
127  break;
128  case 'n': if ((Opt.KeyNum = atoi(optarg)) <= 0) Usage(1, "-n", optarg);
129  break;
130  case 's': if ((int)strlen(optarg) > 1 || !index("cgknux", *optarg))
131  Usage(1, "-s", optarg);
132  Opt.Sort = *optarg;
133  break;
134  case 'u': Opt.KeyUser = optarg;
135  break;
136  case 'x': if ((Opt.Expdt = getXDate(optarg)) < 0
137  || Opt.Expdt < (time(0)+60)) Usage(1, "-x", optarg);
138  break;
139  default: if (index(validOpts, optopt)) Usage(1, argv[optind-1], optarg);
140  else {eMsg("Invalid option '" <<argv[optind-1] <<"'");
141  Usage(1);
142  }
143  }
144  }
145 
146 // Make sure and opreration has been specified
147 //
148  if (optind >= argc) {eMsg("Action not specified."); Usage(1);}
149 
150 // Verify the action
151 //
152  if (!strcmp(argv[optind], "add")) doIt = doAdd;
153  else if (!strcmp(argv[optind], "install")) doIt = doInst;
154  else if (!strcmp(argv[optind], "del")) doIt = doDel;
155  else if (!strcmp(argv[optind], "list")) doIt = doList;
156  else Usage(1, "parameter", argv[optind]);
157  Opt.Action = argv[optind++];
158 
159 // Make sure keyname is not too long
160 //
161  if (Opt.KeyName && (int)strlen(Opt.KeyName) >= XrdSecsssKT::ktEnt::NameSZ)
162  {eMsg("Key name must be less than " <<XrdSecsssKT::ktEnt::NameSZ
163  << " characters.");
164  exit(4);
165  }
166 
167 // Make sure username is not too long
168 //
169  if (Opt.KeyUser && (int)strlen(Opt.KeyUser) >= XrdSecsssKT::ktEnt::UserSZ)
170  {eMsg("User name must be less than " <<XrdSecsssKT::ktEnt::UserSZ
171  << " characters.");
172  exit(4);
173  }
174 
175 // Make sure group name is not too long
176 //
177  if (Opt.KeyGrup && (int)strlen(Opt.KeyGrup) >= XrdSecsssKT::ktEnt::GrupSZ)
178  {eMsg("group name must be less than " <<XrdSecsssKT::ktEnt::GrupSZ
179  << " characters.");
180  exit(4);
181  }
182 
183 // Provide default keyfile if none specified
184 //
185  if (optind < argc) Opt.KeyFile = argv[optind++];
186  else Opt.KeyFile = XrdSecsssKT::genFN();
187 
188 // Perform the action
189 //
190  switch(doIt)
191  {case doAdd: rc = XrdSecsssAdmin_addKey(Opt); break;
192  case doDel: rc = XrdSecsssAdmin_delKey(Opt); break;
193  case doInst: rc = XrdSecsssAdmin_insKey(Opt); break;
194  case doList: rc = XrdSecsssAdmin_lstKey(Opt); break;
195  default: rc = 16; eMsg("Internal processing error!");
196  }
197 
198 // All done
199 //
200  if (Opt.kTab) delete Opt.kTab;
201  exit(rc);
202 }
203 
204 /******************************************************************************/
205 /* g e t X D a t e */
206 /******************************************************************************/
207 
208 time_t getXDate(const char *cDate)
209 {
210  struct tm myTM;
211  char *eP;
212  long theVal;
213 
214 // if no slashes then this is number of days
215 //
216  if (!index(cDate, '/'))
217  {theVal = strtol(cDate, &eP, 10);
218  if (errno || *eP) return -1;
219  if (theVal) theVal = XrdSysTimer::Midnight() + (86400*theVal);
220  return static_cast<time_t>(theVal);
221  }
222 
223 // Do a date conversion
224 //
225  eP = strptime(cDate, "%D", &myTM);
226  if (*eP) return -1;
227  return mktime(&myTM);
228 }
229 
230 /******************************************************************************/
231 /* i s N o */
232 /******************************************************************************/
233 
234 int isNo(int dflt, const char *Msg1, const char *Msg2, const char *Msg3)
235 {
236  char Answer[8];
237 
238  std::cerr <<XrdpgmName <<": " <<Msg1 <<Msg2 <<Msg3;
239  std::cin.getline(Answer, sizeof(Answer));
240  if (!*Answer) return dflt;
241 
242  if (!strcmp("y",Answer) || !strcmp("ye",Answer) || !strcmp("yes",Answer))
243  return 0;
244  return 1;
245 }
246 
247 /******************************************************************************/
248 /* U s a g e */
249 /******************************************************************************/
250 
251 void Usage(int rc, const char *opn, const char *opv)
252 {
253 // Check if we need to issue a message here
254 //
255  if (opn)
256  {if (opv) eMsg("Invalid " <<opn <<" argument - " <<opv);
257  else eMsg(opn <<" argument not specified.");
258  }
259 
260 std::cerr <<"\nUsage: " <<XrdpgmName <<" [options] action\n";
261 std::cerr <<"\nOptions: [-d] [-g grpname] [-h hold] [-k keyname] [-l keylen] [-n keynum]";
262 std::cerr <<"\n [-s {c|g|k|n|u|x}] [-u usrname] [-x {days | mm/dd/yy}]" <<std::endl;
263 std::cerr <<"\nActions: {add | del | install | list} [keyfn]" <<std::endl;
264 exit(rc);
265 }
266 
267 /******************************************************************************/
268 /* X r d S e c s s s A d m i n _ a d d K e y */
269 /******************************************************************************/
270 
272 {
273  XrdOucErrInfo eInfo;
274  XrdSecsssKT::ktEnt *ktEnt;
275  int retc, numKeys, numTot, numExp;
276 
277 // Allocate the initial keytab
278 //
279  Opt.kTab = new XrdSecsssKT(&eInfo, Opt.KeyFile, XrdSecsssKT::isAdmin);
280  if ((retc = eInfo.getErrInfo()))
281  {if (retc != ENOENT || isNo(0, "Keyfile '", Opt.KeyFile,
282  "' does not exist. Create it? (y | n): ")) return 4;
283  }
284 
285 // Construct a new KeyTab entry
286 //
287  ktEnt = new XrdSecsssKT::ktEnt;
288  strcpy(ktEnt->Data.Name, (Opt.KeyName ? Opt.KeyName : "nowhere"));
289  strcpy(ktEnt->Data.User, (Opt.KeyUser ? Opt.KeyUser : "nobody"));
290  strcpy(ktEnt->Data.Grup, (Opt.KeyGrup ? Opt.KeyGrup : "nogroup"));
293  else if (Opt.KeyLen < 4) ktEnt->Data.Len = 4;
294  else ktEnt->Data.Len = Opt.KeyLen/4*4;
295  ktEnt->Data.Exp = Opt.Expdt;
296  Opt.kTab->addKey(*ktEnt);
297 
298 // Now rewrite the file
299 //
300  if ((retc = Opt.kTab->Rewrite(Opt.Keep, numKeys, numTot, numExp)))
301  {eMsg("Unable to add key to '" <<Opt.KeyFile <<"'; " <<XrdSysE2T(retc));
302  retc = 8;
303  } else {
304  eMsg(numKeys <<(numKeys == 1 ? " key":" keys") <<" out of "
305  <<numTot <<" kept (" <<numExp <<" expired).");
306  }
307 
308 // All done
309 //
310  return retc;
311 }
312 
313 /******************************************************************************/
314 /* X r d S e c s s s A d m i n _ d e l K e y */
315 /******************************************************************************/
316 
318 {
319  XrdOucErrInfo eInfo;
320  XrdSecsssKT::ktEnt ktEnt;
321  int retc, numKeys, numTot, numExp, numDel;
322 
323 // Allocate the initial keytab
324 //
325  Opt.kTab = new XrdSecsssKT(&eInfo, Opt.KeyFile, XrdSecsssKT::isAdmin);
326  if ((retc = eInfo.getErrInfo()))
327  {if (retc == ENOENT)
328  {eMsg("Keyfile '" <<Opt.KeyFile <<"' does not exist.");}
329  return 4;
330  }
331 
332 // Construct deletion reference
333 //
334  if (Opt.KeyName) strcpy(ktEnt.Data.Name, Opt.KeyName);
335  if (Opt.KeyUser) strcpy(ktEnt.Data.User, Opt.KeyUser);
336  if (Opt.KeyGrup) strcpy(ktEnt.Data.Grup, Opt.KeyGrup);
337  ktEnt.Data.ID = static_cast<long long>(Opt.KeyNum);
338 
339 // Delete the keys from the key table
340 //
341  if (!(numDel = Opt.kTab->delKey(ktEnt)))
342  {eMsg("No matching key(s) found.");
343  return 4;
344  }
345 
346 // It's possible that all of the keys were deleted. Check for that
347 //
348  if (Opt.kTab->keyList() == 0)
349  {if (isNo(1, "No keys will remain in ", Opt.KeyFile,
350  ". Delete file? (n | y): "))
351  {eMsg("No keys deleted!"); return 2;}
352  unlink(Opt.KeyFile);
353  return 0;
354  }
355 
356 // Now rewrite the file
357 //
358  if ((retc = Opt.kTab->Rewrite(Opt.Keep, numKeys, numTot, numExp)))
359  {eMsg("Unable to del key from '" <<Opt.KeyFile <<"'; " <<XrdSysE2T(retc));
360  retc = 8;
361  } else {
362  eMsg(numKeys <<(numKeys == 1 ? " key":" keys") <<" out of "
363  <<(numTot+numDel) <<" kept (" <<numExp <<" expired).");
364  }
365 
366 // All done
367 //
368  return retc;
369 }
370 
371 /******************************************************************************/
372 /* X r d S e c s s s A d m i n _ i n s K e y */
373 /******************************************************************************/
374 
376 {
378  XrdSecsssKT::ktEnt *ktP);
379  XrdOucErrInfo eInfo;
380  XrdSecsssKT::ktEnt *ktP;
381  int retc, numKeys = 0, numTot, numExp;
382 
383 // Allocate the initial keytab
384 //
385  Opt.kTab = new XrdSecsssKT(&eInfo, 0, XrdSecsssKT::isAdmin);
386  if ((retc = eInfo.getErrInfo())) return 4;
387 
388 // Check if we need to trim the keytab to a particular key
389 //
390  if (Opt.KeyName || Opt.KeyUser || Opt.KeyGrup)
391  {ktP = Opt.kTab->keyList();
392  while(ktP)
393  {if (!XrdSecsssAdmin_isKey(Opt, ktP)) ktP->Data.Name[0] = '\0';
394  else numKeys++;
395  ktP = ktP->Next;
396  }
397  if (!numKeys)
398  {eMsg("No keys named " <<Opt.KeyName <<" found to install.");
399  return 8;
400  }
401  }
402 
403 // Now rewrite the file
404 //
405  Opt.kTab->setPath(Opt.KeyFile);
406  if ((retc = Opt.kTab->Rewrite(Opt.Keep, numKeys, numTot, numExp)))
407  {eMsg("Unable to install keytab '" <<Opt.KeyFile <<"'; " <<XrdSysE2T(retc));
408  retc = 8;
409  } else {
410  eMsg(numKeys <<(numKeys == 1 ? " key":" keys") <<" out of "
411  <<numTot <<" installed (" <<numExp <<" expired).");
412  }
413 
414 // All done
415 //
416  return retc;
417 }
418 
419 /******************************************************************************/
420 /* X r d S e c s s s A d m i n _ i s K e y */
421 /******************************************************************************/
422 
424  XrdSecsssKT::ktEnt *ktP)
425 {
426  if (Opt.KeyName && strcmp(ktP->Data.Name, Opt.KeyName)) return 0;
427  if (Opt.KeyUser && strcmp(ktP->Data.User, Opt.KeyUser)) return 0;
428  if (Opt.KeyGrup && strcmp(ktP->Data.Grup, Opt.KeyGrup)) return 0;
429  return 1;
430 }
431 
432 /******************************************************************************/
433 /* X r d S e c s s s A d m i n _ H e r e */
434 /******************************************************************************/
435 
437  XrdSecsssKT::ktEnt *ktS)
438 {
439  int n;
440  char *sf1, *sf2;
441 
442  switch(sType)
443  {case 'c': return ktX->Data.Crt < ktS->Data.Crt;
444  case 'g': sf1 = ktX->Data.Grup; sf2 = ktS->Data.Grup; break;
445  case 'k': sf1 = ktX->Data.Name; sf2 = ktS->Data.Name; break;
446  case 'n': return (ktX->Data.ID & 0x7fffffff) < (ktS->Data.ID & 0x7fffffff);
447  case 'u': sf1 = ktX->Data.User; sf2 = ktS->Data.User; break;
448  case 'x': return ktX->Data.Exp < ktS->Data.Exp;
449  default: return 0;
450  }
451 
452  if ((n = strcmp(sf1, sf2))) return n < 0;
453  return (ktX->Data.ID & 0x7fffffff) < (ktS->Data.ID & 0x7fffffff);
454 }
455 
456 /******************************************************************************/
457 /* X r d S e c s s s A d m i n _ l s t K e y */
458 /******************************************************************************/
459 
461 {
462  static const char Hdr1[] =
463  " Number Len Date/Time Created Expires Keyname User & Group\n";
464 // 12345678901 123 mm/dd/yy hh:mm:ss mm/dd/yy
465  static const char Hdr2[] =
466  " ------ --- --------- ------- -------- -------\n";
467 
469  XrdSecsssKT::ktEnt *ktP);
470  XrdOucErrInfo eInfo;
471  XrdSecsssKT::ktEnt *ktP, *ktSort = 0, *ktS, *ktSP, *ktX;
472  char crfmt[] = "%D %T", exfmt[] = "%D";
473  char buff[128], crbuff[64], exbuff[16];
474  int retc, pHdr = 1;
475 
476 // Allocate the initial keytab
477 //
478  Opt.kTab = new XrdSecsssKT(&eInfo, Opt.KeyFile, XrdSecsssKT::isAdmin);
479  if ((retc = eInfo.getErrInfo()))
480  {if (retc == ENOENT)
481  {eMsg("Keyfile '" <<Opt.KeyFile <<"' does not exist.");}
482  return 4;
483  }
484 
485 // Obtain the keytab list
486 //
487  if ((ktP = Opt.kTab->keyList()))
488  {ktSort = ktP; ktP = ktP->Next; ktSort->Next = 0;}
489 
490 // Sort the list
491 //
492  while(ktP)
493  {ktS = ktSort; ktSP = 0; ktX = ktP; ktP = ktP->Next; ktX->Next = 0;
494  while(ktS)
495  {if (XrdSecsssAdmin_Here(Opt.Sort, ktX, ktS))
496  {if (ktSP) {ktX->Next = ktS; ktSP->Next = ktX;}
497  else {ktX->Next = ktSort; ktSort = ktX;}
498  break;
499  }
500  ktSP = ktS; ktS = ktS->Next;
501  }
502  if (!ktS) ktSP->Next = ktX;
503  }
504 
505 // List the keys
506 //
507  ktP = ktSort;
508  while(ktP)
509  {if (XrdSecsssAdmin_isKey(Opt, ktP))
510  {if (pHdr) {std::cout <<Hdr1 <<Hdr2; pHdr = 0;}
511  sprintf(buff, "%11lld %3d ", (ktP->Data.ID & 0x7fffffff), ktP->Data.Len);
512  strftime(crbuff, sizeof(crbuff), crfmt, localtime(&ktP->Data.Crt));
513  if (!ktP->Data.Exp) strcpy(exbuff, "--------");
514  else strftime(exbuff,sizeof(exbuff),exfmt,localtime(&ktP->Data.Exp));
515  std::cout <<buff <<crbuff <<' ' <<exbuff <<' ' <<ktP->Data.Name <<' '
516  <<ktP->Data.User <<' ' <<ktP->Data.Grup <<std::endl;
517  }
518  ktP = ktP->Next;
519  }
520 
521 // Check if we printed anything
522 //
523  if (pHdr)
524  {if (Opt.KeyName) eMsg(Opt.KeyName <<" key not found in " <<Opt.KeyFile);
525  else eMsg("No keys found in " <<Opt.KeyFile);
526  }
527  return 0;
528 }
529 
530 
int optopt
int optind
int unlink(const char *path)
int XrdSecsssAdmin_insKey(XrdsecsssAdmin_Opts &Opt)
int main(int argc, char **argv)
int XrdSecsssAdmin_Here(char sType, XrdSecsssKT::ktEnt *ktX, XrdSecsssKT::ktEnt *ktS)
int isNo(int dflt, const char *Msg1, const char *Msg2, const char *Msg3)
int XrdSecsssAdmin_isKey(XrdsecsssAdmin_Opts &Opt, XrdSecsssKT::ktEnt *ktP)
static const char * XrdpgmName
int XrdSecsssAdmin_lstKey(XrdsecsssAdmin_Opts &Opt)
int XrdSecsssAdmin_addKey(XrdsecsssAdmin_Opts &Opt)
time_t getXDate(const char *cDate)
int XrdSecsssAdmin_delKey(XrdsecsssAdmin_Opts &Opt)
#define eMsg(x)
void Usage(int rc, const char *opn, const char *opv)
const char * XrdSysE2T(int errcode)
Definition: XrdSysE2T.cc:104
static const int UserSZ
Definition: XrdSecsssKT.hh:50
struct XrdSecsssKT::ktEnt::ktData Data
static const int GrupSZ
Definition: XrdSecsssKT.hh:51
static const int maxKLen
Definition: XrdSecsssKT.hh:48
static const int NameSZ
Definition: XrdSecsssKT.hh:49
void setPath(const char *Path)
Definition: XrdSecsssKT.hh:111
int delKey(ktEnt &ktDel)
Definition: XrdSecsssKT.cc:185
int Rewrite(int Keep, int &numKeys, int &numTot, int &numExp)
Definition: XrdSecsssKT.cc:339
static char * genFN()
Definition: XrdSecsssKT.cc:249
ktEnt * keyList()
Definition: XrdSecsssKT.hh:101
void addKey(ktEnt &ktNew)
Definition: XrdSecsssKT.cc:158
static time_t Midnight(time_t tnow=0)
Definition: XrdSysTimer.cc:58
const char * KeyGrup
XrdSecsssKT * kTab
const char * KeyFile
const char * KeyUser
const char * KeyName