/******************************************************************************/ /* */ /* X r d S e c p w d S r v A d m i n . c c */ /* */ /* (c) 2005 by the Board of Trustees of the Leland Stanford, Jr., University */ /* Produced by Gerri Ganis for CERN */ /* */ /* This file is part of the XRootD software suite. */ /* */ /* XRootD is free software: you can redistribute it and/or modify it under */ /* the terms of the GNU Lesser General Public License as published by the */ /* Free Software Foundation, either version 3 of the License, or (at your */ /* option) any later version. */ /* */ /* XRootD is distributed in the hope that it will be useful, but WITHOUT */ /* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */ /* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public */ /* License for more details. */ /* */ /* You should have received a copy of the GNU Lesser General Public License */ /* along with XRootD in a file called COPYING.LESSER (LGPL license) and file */ /* COPYING (GPL license). If not, see . */ /* */ /* The copyright holder's institutional names and contributor's names may not */ /* be used to endorse or promote products derived from this software without */ /* specific prior written permission of the institution or contributor. */ /******************************************************************************/ // ---------------------------------------------------------------------- // // // // Password file administration // // // // Use this application to: // // // // - Create / Modify a password file for servers under your // // administration. // // Default location and name $(HOME)/.xrd/pwdadmin] // // // // XrdSecpwdSrvAdmin [] // // // // NB: permissions must be such that the file is // // readable and writable by owner only, e.g. 0600 // // // // // // - Create / Modify a password file for servers enabled to verify // // user passwords [default location and name $(HOME)/.xrd/pwduser] // // // // XrdSecpwdSrvAdmin -m user [] // // // // NB: copy the file on the server machine if you are producing // // it elsewhere; permissions must be such that the file is // // writable by owner only, e.g. 0644 // // // // // // - Create / Modify a autologin file // // [default location and name $(HOME)/.xrd/pwdnetrc] // // // // XrdSecpwdSrvAdmin -m netrc [] // // // // NB: permissions must be such that the file is // // readable and writable by owner only, e.g. 0600 // // // // - Create / Modify the file with server public cipher initiators // // [default location and name $(HOME)/.xrd/pwdsrvpuk] // // // // XrdSecpwdSrvAdmin -m srvpuk [] // // // // NB: permissions must be such that the file is // // writable by owner only, e.g. 0644 // // // // // // Author: G.Ganis, 2005 // // ---------------------------------------------------------------------- // #include #include #include #include #include #include #include #include #include #include #include #include "XrdOuc/XrdOucString.hh" #include "XrdSut/XrdSutAux.hh" #include "XrdSut/XrdSutPFEntry.hh" #include "XrdSut/XrdSutPFile.hh" #include "XrdSut/XrdSutRndm.hh" #include "XrdCrypto/XrdCryptoCipher.hh" #include "XrdCrypto/XrdCryptoFactory.hh" // // enum enum kModes { kM_undef = 0, kM_admin = 1, kM_user, kM_netrc, kM_srvpuk, kM_help }; const char *gModesStr[] = { "kM_undef", "kM_admin", "kM_user", "kM_netrc", "kM_srvpuk", "kM_help" }; enum kActions { kA_undef = 0, kA_add = 1, kA_update, kA_read, kA_remove, kA_disable, kA_copy, kA_trim, kA_browse }; const char *gActionsStr[] = { "kA_undef", "kA_add", "kA_update", "kA_read", "kA_remove", "kA_disable", "kA_copy", "kA_trim", "kA_browse" }; // // Globals int DebugON = 1; XrdOucString DirRef = "~/.xrd/"; XrdOucString AdminRef = "pwdadmin"; XrdOucString UserRef = "pwduser"; XrdOucString NetRcRef = "pwdnetrc"; XrdOucString SrvPukRef= "pwdsrvpuk"; XrdOucString GenPwdRef= "/genpwd/"; XrdOucString GenPukRef= "/genpuk/"; XrdOucString IDTag = "+++SrvID"; XrdOucString EmailTag = "+++SrvEmail"; XrdOucString HostTag = "+++SrvHost"; XrdOucString PukTag = "+++SrvPuk"; XrdOucString PwdFile = ""; XrdOucString PukFile = "/home/ganis/.xrd/genpuk/puk.07May2005-0849"; int Mode = kM_undef; int Action = kA_undef; int NoBackup = 1; XrdOucString NameTag = ""; XrdOucString CopyTag = ""; XrdOucString File = ""; XrdOucString Path = ""; XrdOucString Dir = ""; XrdOucString SrvID = ""; XrdOucString SrvName = ""; XrdOucString Email = ""; XrdOucString IterNum = ""; bool Backup = 1; bool DontAsk = 0; bool Force = 0; bool Passwd = 1; bool Change = 1; bool Random = 0; bool SavePw = 1; bool SetID = 0; bool SetEmail = 0; bool SetHost = 0; bool Create = 0; bool Confirm = 1; bool Import = 0; bool Hash = 1; bool ChangePuk = 0; bool ChangePwd = 0; bool ExportPuk = 0; #define NCRYPTMAX 10 // max number of crypto factories XrdOucString DefCrypto = "ssl"; XrdOucString CryptList = ""; int ncrypt = 0; // number of available crypto factories XrdOucString CryptMod[NCRYPTMAX] = {""}; // .. and their names XrdCryptoCipher **RefCip = 0; // .. and their ciphers XrdCryptoFactory **CF = 0; XrdCryptoKDFun_t KDFun = 0; XrdCryptoKDFunLen_t KDFunLen = 0; void Menu(int opt = 0); int ParseArguments(int argc, char **argv); void ParseCrypto(); bool CheckOption(XrdOucString opt, const char *ref, int &ival); bool AddPassword(XrdSutPFEntry &ent, XrdOucString salt, XrdOucString &ranpwd, bool random, bool checkpw, bool &newpw); bool AddPassword(XrdSutPFEntry &ent, bool &newpw, const char *pwd = 0); void SavePasswd(XrdOucString tag, XrdOucString pwd, bool onetime); bool ReadPasswd(XrdOucString &tag, XrdOucString &pwd, int &st); bool ReadPuk(int &npuk, XrdOucString *tpuk, XrdOucString *puk); int GeneratePuk(); bool SavePuk(); bool ReadPuk(); bool ExpPuk(const char *puk = 0, bool read = 1); bool GetEntry(XrdSutPFile *ff, XrdOucString tag, XrdSutPFEntry &ent, bool &check); bool AskConfirm(const char *msg1, bool defact, const char *msg2 = 0); int LocateFactoryIndex(char *tag, int &id); #define PRT(x) {cerr < 32) SrvID.erase(32); } else { PRT("Server ID will be generated randomly. It can be changed"); PRT("at any time with 'add -srvID '."); // // Set random ID XrdSutRndm::Init(); XrdSutRndm::GetString(1,8,SrvID); // // Add local user name struct passwd *pw = getpwuid(getuid()); if (pw) { SrvID.insert(':',0); SrvID.insert(pw->pw_name,0); } } } else if (DontAsk) { // This is a force creation where no prompt request can be answered SetID = 0; } PRT("Server ID: " << SrvID); if (SrvID.length() > 0) { // // Fill entry ent.SetName(IDTag.c_str()); ent.status = kPFE_special; ent.cnt = 1; ent.buf1.SetBuf(SrvID.c_str(),SrvID.length()+1); // // Write entry ent.mtime = time(0); ff.WriteEntry(ent); PRT(" File successfully created with server ID set to: " <ID(); // // Serialize in a buffer XrdSutBucket *bck = RefCip[i]->AsBucket(); if (bck) { // // Prepare Entry ent.SetName(tag.c_str()); ent.status = kPFE_special; ent.cnt = 2; // protected ent.buf1.SetBuf(bck->buffer,bck->size); // // Write entry ent.mtime = time(0); ff.WriteEntry(ent); PRT(" Server Puk saved for crypto: "<Name()); delete bck; bck = 0; } } } // // Backup also on separate file if (!SavePuk()) { PRT("// Problems with puk backup "); } } } else { PRT(" File successfully created "); } } // If admin, check for special entries // (Server Unique ID, Email, Host name) if (Mode == kM_admin) { // // Ref ciphers ent.Reset(); nm = ff.SearchEntries(PukTag.c_str(),0); if (nm) { int *ofs = new int[nm]; ff.SearchEntries(PukTag.c_str(),0,ofs,nm); for ( i = 0; i < nm ; i++) { nr = ff.ReadEntry(ofs[i],ent); if (nr > 0) { XrdSutBucket bck; bck.SetBuf(ent.buf1.buf,ent.buf1.len); // Locate factory ID int id = 0; int ii = LocateFactoryIndex(ent.name, id); if (ii < 0) { PRT("// Factory ID not found: corruption ?"); exit(1); } if (!(RefCip[i] = CF[ii]->Cipher(&bck))) { PRT("// Could not instantiate cipher for factory "<Name()); exit(1); } } } } else { PRT("// Ref puk ciphers not found: corruption ?"); exit(1); } if (ff.ReadEntry(IDTag.c_str(),ent) <= 0 && !SetID) { PRT(" Unique ID missing: 'add -srvID' to set it"); } else if (!SetID) { SrvID.insert(ent.buf1.buf,0,ent.buf1.len); } // // Unique ID ent.Reset(); if (ff.ReadEntry(IDTag.c_str(),ent) <= 0 && !SetID) { PRT(" Unique ID missing: 'add -srvID' to set it"); } else if (!SetID) { SrvID.insert(ent.buf1.buf,0,ent.buf1.len); } // // Email ent.Reset(); if (ff.ReadEntry(EmailTag.c_str(),ent) <= 0 && !SetEmail) { PRT(" Contact E-mail not set: 'add -email ' to set it"); } else if (!SetEmail) { Email.insert(ent.buf1.buf,0,ent.buf1.len); } // // Server Host name ent.Reset(); if (ff.ReadEntry(HostTag.c_str(),ent) <= 0 && !SetHost) { PRT(" Local host name not set: 'add -host ' to set it"); } else if (!SetHost) { SrvName.insert(ent.buf1.buf,0,ent.buf1.len); } } switch (Action) { case kA_update: // Like 'add', forcing write case kA_add: if (Action == kA_update) Force = 1; // // Add / Update entry // // If admin, check first if we are required to update/create // some special entry (Server Unique ID, Email, Host Name) if (Mode == kM_admin) { // // Export current Server PUK if (ExportPuk) { if (!ExpPuk()) { PRT("// Could not export public keys"); } // // We are done break; } // // Server PUK ent.Reset(); if (ChangePuk) { if (!DontAsk && !AskConfirm("Override server PUK?",0,0)) break; // // If we are given a file name, try import from the file if (Import && PukFile.length() > 0) { if (!ReadPuk()) { PRT("// Problem importing puks from "< 0) { // // Locate factory ID int id; int j = LocateFactoryIndex(ent.name,id); if (j < 0) break; // Serialize in a buffer XrdSutBucket *bck = RefCip[j]->AsBucket(); if (bck) { // Shift up buffer content (buf 4 is removed) if (ent.buf4.buf) delete[] ent.buf4.buf; ent.buf4.buf = ent.buf3.buf; ent.buf4.len = ent.buf3.len; ent.buf3.buf = ent.buf2.buf; ent.buf3.len = ent.buf2.len; ent.buf2.buf = ent.buf1.buf; ent.buf2.len = ent.buf1.len; // fill buf 1 with new puk ent.buf1.SetBuf(bck->buffer,bck->size); // // Write entry ent.mtime = time(0); ff.WriteEntry(ent); PRT(" Server Puk updated for crypto: "<Name()); delete bck; bck = 0; } // // Flag user entries char stag[4]; sprintf(stag,"*_%d",id); int nofs = ff.SearchEntries(stag,2); if (nofs > 0) { int *uofs = new int[nofs]; ff.SearchEntries(stag,2,uofs,nofs); XrdSutPFEntry uent; int k = 0, nnr = 0; for (; k < nofs; k++) { uent.Reset(); nnr = ff.ReadEntry(uofs[k],uent); if (nnr > 0 && !strstr(uent.name,PukTag.c_str())) { char c = 0; if (uent.buf4.buf) { c = *(uent.buf4.buf); c++; if (c > 4) c = 1; *(uent.buf4.buf) = c; } else { uent.buf4.buf = new char[1]; uent.buf4.len = 1; *(uent.buf4.buf) = 2; } // Write entry uent.mtime = time(0); ff.WriteEntry(uent); } } } } else { PRT("// warning: problems reading entry: corruption?"); break; } } } else { PRT("// WARNING: No entry for tag '"<' "); break; } if (!ReadPuk(nHostPuk,TagHostPuk,HostPuk)) break; // // Now we loop over tags for (i = 0; i < nHostPuk; i++) { // Check if not already existing ent.Reset(); if (GetEntry(&ff,TagHostPuk[i],ent,check)) { break; } // Fill in new puk ent.buf1.SetBuf(HostPuk[i].c_str(),HostPuk[i].length()+1); // Write entry ent.mtime = time(0); ff.WriteEntry(ent); if (check) { PRT("// Server puk "< 0) { // Insert non default iteration number in salt salt.insert(IterNum,0); } } // for ( i = 0; i < ncrypt; i++ ) { // Get hook to crypto factory CF[i] = XrdCryptoFactory::GetCryptoFactory(CryptMod[i].c_str()); if (!CF[i]) { PRT("Hook for crypto factory undefined: "<KDFun(); KDFunLen = CF[i]->KDFunLen(); if (!KDFun || !KDFunLen) { PRT("Error resolving one-way hash functions "); break; } // // Build tag tag = NameTag + '_'; tag += CF[i]->ID(); // Check if not already existing ent.Reset(); if (GetEntry(&ff,tag,ent,checkpwd)) { break; } if (Mode == kM_netrc) { // If just a request for password change not much to do if (ChangePwd) { if (!checkpwd) break; else // Update the status ent.status = kPFE_onetime; } else { // Reset status and cnt if (pwdimp) ent.status = entst; else ent.status = kPFE_ok; ent.cnt = 0; // // Fill with password if (!AddPassword(ent, newpw, pwdimp)) { PRT("Error creating new password: "< 0) { PRT("// #:"< 0) { // Disable entry ent.status = kPFE_disabled; ent.cnt = 0; ent.buf1.SetBuf(); ent.buf2.SetBuf(); ent.buf3.SetBuf(); ent.buf4.SetBuf(); // Save (or update) entry ent.mtime = time(0); ff.WriteEntry(ent); PRT("// Entry for tag '"<SetName(CopyTag.c_str()); // // Write entry nent->mtime = time(0); ff.WriteEntry(*nent); PRT("// Entry for tag '"<name<< "' created"); delete nent; } else { PRT("// Cannot create new entry: out of memory"); break; } PRT("//"); PRT("//-----------------------------------------------------" "--------------------//"); break; case kA_trim: case kA_browse: default: // // Trim the file first before browsing if (Action == kA_trim) ff.Trim(); // // Browse ff.Browse(); break; } exit(0); } void Menu(int opt) { // Print the menu // Options: 0 intro w/ head/tail // 1 intro w/o head/tail // 2 keywords // Head if (opt == 0) { PRT("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); PRT("+ +"); PRT("+ x r d p w d a d m i n +"); PRT("+ +"); PRT("+ Administration of pwd files +"); } // Intro if (opt <= 1) { PRT("+ +"); PRT("+ Syntax: +"); PRT("+ +"); PRT("+ xrdpwdadmin [-h] [-m ] [options] +"); PRT("+ +"); PRT("+ -h display this menu +"); PRT("+ +"); PRT("+ -m choose mode (admin, user, netrc, srvpuk) [admin] +"); PRT("+ +"); PRT("+ admin: +"); PRT("+ create / modify the main file used by servers +"); PRT("+ started from this account to validate clients +"); PRT("+ credentials. Default location and name: +"); PRT("+ $(HOME)/.xrd/pwdadmin +"); PRT("+ +"); PRT("+ NB: file must readable and writable by owner +"); PRT("+ only e.g. 0600 +"); PRT("+ +"); PRT("+ user: +"); PRT("+ create / modify local file used by servers +"); PRT("+ to validate this user credentials. +"); PRT("+ Default location and name: +"); PRT("+ $(HOME)/.xrd/pwduser +"); PRT("+ +"); PRT("+ NB: the file must be copied on the server machine +"); PRT("+ if produced elsewhere; file must be writable +"); PRT("+ by the owner only, e.g. 0644 +"); PRT("+ +"); PRT("+ netrc: +"); PRT("+ create / modify local autologin file +"); PRT("+ Default location and name: +"); PRT("+ $(HOME)/.xrd/pwdnetrc +"); PRT("+ +"); PRT("+ NB: file must readable and writable by owner +"); PRT("+ only e.g. 0600 +"); PRT("+ +"); PRT("+ srvpuk: +"); PRT("+ create / modify local file with known server +"); PRT("+ public cipher initializers. +"); PRT("+ Default location and name: +"); PRT("+ $(HOME)/.xrd/pwdsrvpuk +"); PRT("+ +"); PRT("+ NB: file must be writable by the owner only +"); PRT("+ e.g. 0644 +"); } // Intro if (opt <= 2) { PRT("+ +"); PRT("+ Options: +"); PRT("+ +"); PRT("+ add [-[no]force] [-[no]random] [-[no]savepw] +"); PRT("+ add entry with tag ; the application prompts +"); PRT("+ for the password +"); PRT("+ +"); PRT("+ add -import +"); PRT("+ add entry with tag importing the pwd from +"); PRT("+ the file send by the server administrator +"); PRT("+ [netrc only] +"); PRT("+ +"); PRT("+ add -import +"); PRT("+ add new server key importing the key from +"); PRT("+ the file send by the server administrator +"); PRT("+ [srvpuk only] +"); PRT("+ +"); PRT("+ update [options] +"); PRT("+ equivalent to 'add -force' +"); PRT("+ +"); PRT("+ read +"); PRT("+ list some information of entry associated with tag +"); PRT("+ (status, count, date of last change, buffer +"); PRT("+ lengths); buffer contents not listed +"); PRT("+ +"); PRT("+ remove +"); PRT("+ Make entry associated with tag inactive +"); PRT("+ (Spce is recovered during next trim operation) +"); PRT("+ +"); PRT("+ copy +"); PRT("+ Create new entry with tag and content of +"); PRT("+ existing entry with tag +"); PRT("+ +"); PRT("+ trim [-nobackup] +"); PRT("+ Trim the file content eliminating all the inactive +"); PRT("+ entries; a backup is created in .bak unless +"); PRT("+ the option '-nobackup' is specified +"); PRT("+ +"); PRT("+ browse +"); PRT("+ list a table about the file content +"); } // Intro if (opt <= 3) { PRT("+ +"); PRT("+ -dontask +"); PRT("+ do not prompt for questions: when in doubt use +"); PRT("+ defaults or fail +"); PRT("+ [default: ask] +"); PRT("+ -force +"); PRT("+ overwrite entry if it exists already +"); PRT("+ [default: do not overwrite] +"); PRT("+ -[no]change +"); PRT("+ do [not] require user to change info on first use +"); PRT("+ [default: admin: change / user: no change +"); PRT("+ -crypto [-]|[-]|... +"); PRT("+ create information for the given crypto modules +"); PRT("+ ('|' separated list) in addition to default ones +"); PRT("+ (normally ssl and local); use '-' in front to avoid +"); PRT("+ avoid creating a entry for a module; one entry is +"); PRT("+ for each module with effective tag of the form +"); PRT("+ name_ [default list: ssl] +"); PRT("+ [default: create backup] +"); } // Tail PRT("+ +"); PRT("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); } int ParseArguments(int argc, char **argv) { // Parse application arguments filling relevant global variables bool changeset = 0; bool randomset = 0; bool savepwset = 0; bool randomid = 0; // Number of arguments if (argc < 0 || !argv[0]) { PRT("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); PRT("+ Insufficient number or arguments! +"); PRT("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); // Print main menu Menu(0); return 1; } --argc; ++argv; // // Loop over arguments while ((argc >= 0) && (*argv)) { XrdOucString opt = ""; int ival = -1; if(*(argv)[0] == '-') { opt = *argv; opt.erase("-"); if (CheckOption(opt,"m",ival)) { if (Mode != kM_undef) { PRT("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); PRT("+ Only one valid '-m' option allowed: ignoring +"); PRT("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); --argc; ++argv; if (argc >= 0 && (*argv && *(argv)[0] == '-')) { argc++; argv--; } } --argc; ++argv; if (argc >= 0 && (*argv && *(argv)[0] != '-')) { XrdOucString mode = *argv; if (CheckOption(mode,"admin",ival)) { Mode = kM_admin; } else if (CheckOption(mode,"user",ival)) { Mode = kM_user; } else if (CheckOption(mode,"netrc",ival)) { Mode = kM_netrc; } else if (CheckOption(mode,"srvpuk",ival)) { Mode = kM_srvpuk; } else if (CheckOption(mode,"help",ival)) { Mode = kM_help; } else { PRT("++++++++++++++++++++++++++++++++++++++" "++++++++++++++++++++++"); PRT("+ Ignoring unrecognized more: "<= 0 && (*argv && *(argv)[0] != '-')) { Path = *argv; } else { PRT("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); PRT("+ Option '-f' requires a file or directory name: ignoring +"); PRT("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); argc++; argv--; } } else if (CheckOption(opt,"dontask",ival)) { DontAsk = ival; } else if (CheckOption(opt,"force",ival)) { Force = ival; } else if (CheckOption(opt,"change",ival)) { Change = ival; changeset = 1; } else if (CheckOption(opt,"passwd",ival)) { Passwd = ival; } else if (CheckOption(opt,"backup",ival)) { Backup = ival; } else if (CheckOption(opt,"random",ival)) { Random = ival; randomset = 1; } else if (CheckOption(opt,"savepw",ival)) { SavePw = ival; savepwset = 1; } else if (CheckOption(opt,"confirm",ival)) { Confirm = ival; } else if (CheckOption(opt,"create",ival)) { Create = ival; } else if (CheckOption(opt,"hash",ival)) { Hash = ival; } else if (CheckOption(opt,"changepuk",ival)) { ChangePuk = ival; } else if (CheckOption(opt,"changepwd",ival)) { ChangePwd = ival; } else if (CheckOption(opt,"exportpuk",ival)) { ExportPuk = ival; } else if (CheckOption(opt,"iternum",ival)) { --argc; ++argv; if (argc >= 0 && (*argv && *(argv)[0] != '-')) { int iter = strtol(*argv,0,10); if (iter > 0 && errno != ERANGE) { IterNum = "$$"; IterNum += *argv; IterNum += "$"; } else { PRT("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); PRT("+ Option '-iternum' requires a positive number: ignoring +"); PRT("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); argc++; argv--; } } else { PRT("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); PRT("+ Option '-iternum' requires a positive number: ignoring +"); PRT("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); argc++; argv--; } } else if (CheckOption(opt,"crypto",ival)) { --argc; ++argv; if (argc >= 0 && (*argv && *(argv)[0] != '-')) { CryptList = *argv; } else { PRT("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); PRT("+ Option '-crypto' requires a list of modules: ignoring +"); PRT("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); argc++; argv--; } } else if (CheckOption(opt,"import",ival)) { --argc; ++argv; if (argc >= 0 && (*argv && *(argv)[0] != '-')) { if (Mode == kM_netrc) { PwdFile = *argv; } else { PukFile = *argv; } Import = 1; } else { PRT("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); PRT("+ Option '-import' requires a file name: ignoring +"); PRT("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); argc++; argv--; } } else if (CheckOption(opt,"srvID",ival)) { --argc; ++argv; SetID = 1; if (argc >= 0 && (*argv && *(argv)[0] != '-')) { SrvID = *argv; } else { SrvID = ""; randomid = 1; argc++; argv--; } } else if (CheckOption(opt,"email",ival)) { --argc; ++argv; if (argc >= 0 && (*argv && *(argv)[0] != '-')) { Email = *argv; SetEmail = 1; } else { PRT("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); PRT("+ Option '-email' requires an email string: ignoring +"); PRT("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); argc++; argv--; } } else if (CheckOption(opt,"host",ival)) { --argc; ++argv; if (argc >= 0 && (*argv && *(argv)[0] != '-')) { SrvName = *argv; SetHost = 1; } else { PRT("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); PRT("+ Option '-host' requires the local host name: ignoring +"); PRT("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); argc++; argv--; } } else { PRT("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); PRT("+ Ignoring unrecognized option: "<<*argv); PRT("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); } } else { // // Action keyword opt = *argv; int iad = -1, iup = -1, ird = -1, irm = -1, idi = -1, icp = -1; if (CheckOption(opt,"add",iad) || CheckOption(opt,"update",iup) || CheckOption(opt,"read",ird) || CheckOption(opt,"remove",irm) || CheckOption(opt,"disable",idi) || CheckOption(opt,"copy",icp)) { Action = (Action == kA_undef && iad == 1) ? kA_add : Action; Action = (Action == kA_undef && iup == 1) ? kA_update : Action; Action = (Action == kA_undef && ird == 1) ? kA_read : Action; Action = (Action == kA_undef && irm == 1) ? kA_remove : Action; Action = (Action == kA_undef && idi == 1) ? kA_disable : Action; Action = (Action == kA_undef && icp == 1) ? kA_copy : Action; --argc; ++argv; if (argc >= 0 && (*argv && *(argv)[0] != '-')) { NameTag = *argv; if (icp == 1) { --argc; ++argv; if (argc >= 0 && (*argv && *(argv)[0] != '-')) { CopyTag = *argv; } else { PRT("+++++++++++++++++++++++++++++++++++++++++" "+++++++++++++++++++"); PRT("+ 'copy': missing destination tag: ignoring" " +"); PRT("+++++++++++++++++++++++++++++++++++++++++" "+++++++++++++++++++"); CopyTag = ""; argc++; argv--; } } } else { NameTag = ""; argc++; argv--; } } else if (CheckOption(opt,"trim",ival)) { Action = kA_trim; } else if (CheckOption(opt,"browse",ival)) { Action = kA_browse; } else { PRT("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); PRT("+ Ignoring unrecognized keyword action: "<pw_name,0); } else { PRT("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); PRT("+ WARNING: could not get local user info for srv ID +"); PRT("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); } } else { if (SrvID.length() > 32) { SrvID.erase(32); PRT("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); PRT("+ WARNING: srv ID too long: truncating to 32 chars: " < 0 && (Mode != kM_admin && Mode != kM_user)) { IterNum = ""; PRT("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); PRT("+ WARNING: ignore iter num change request (not admin/user) +"); PRT("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); } // // Requesting a password change only makes sense in netrc mode if (ChangePwd && Mode != kM_netrc) { ChangePwd = 0; PRT("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); PRT("+ WARNING: ignore password change request (not netrc) +"); PRT("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); } // // If user mode, check if NameTag contains the local user // name: if not, warn the user about possible problems with // servers ignoring this kind of entries for users files if (Mode == kM_user && NameTag.length()) { struct passwd *pw = getpwuid(getuid()); if (pw) { XrdOucString locusr = pw->pw_name; if (NameTag != locusr) { PRT("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); PRT("+ WARNING: name tag does not match local user name: "); PRT("+ "< // ival = -1 in the other cases bool rc = 0; int lref = (ref) ? strlen(ref) : 0; if (!lref) return rc; XrdOucString noref = ref; noref.insert("no",0); ival = -1; if (opt == ref) { ival = 1; rc = 1; } else if (opt == noref) { ival = 0; rc = 1; } return rc; } bool AddPassword(XrdSutPFEntry &ent, XrdOucString salt, XrdOucString &ranpwd, bool random, bool checkpw, bool &newpw) { // Generate (prompting or randomly) new password and add it // to entry ent // If checkpw, make sure that it is different from the existing // one (check is done on the hash, cannot decide if the change // is significant or not). // Return generated random password in ranpwd. // Randoms passwords are 8 char lengths filled with upper and // lower case letters and numbers // If !newpw, the a pwd saved during a previous call is used, // if any. // Return 1 if ok, 0 otherwise. static XrdOucString pwdref; XrdSutPFBuf oldsalt; XrdSutPFBuf oldhash; // // Save existing salt and hash, if required if (checkpw) { if (ent.buf1.len > 0 && ent.buf1.buf) { oldsalt.SetBuf(ent.buf1.buf,ent.buf1.len); if (ent.buf2.len > 0 && ent.buf2.buf) { oldhash.SetBuf(ent.buf2.buf,ent.buf2.len); } else { checkpw = 0; } } else { checkpw = 0; } } // // Save salt ent.buf1.SetBuf(salt.c_str(),salt.length()); // // Prepare to get password XrdOucString passwd = ""; if (newpw || !pwdref.length()) { newpw = 1; pwdref = ""; } char *pwhash = 0; int pwhlen = 0; int natt = 0; while (!passwd.length()) { // // if (natt == kMAXPWDATT) { PRT("AddPassword: max number of attempts reached: "< 0) { // Set a non-default iteration number (we are going to hash // the password with itself) passwd.insert(IterNum,0); } pwdref = passwd; ranpwd = passwd; newpw = 0; checkpw = 0; // not needed } } else { passwd = pwdref; } // Get pw hash encoding password with itself pwhash = new char[(*KDFunLen)()]; pwhlen = (*KDFun)(passwd.c_str(),passwd.length(), passwd.c_str(),passwd.length(),pwhash,0); // // Check the password if required if (checkpw) { // Get hash with old salt char *osahash = new char[(*KDFunLen)()]; // Encode the pw hash with the salt (*KDFun)(pwhash,pwhlen, oldsalt.buf,oldsalt.len,osahash,0); if (!memcmp(oldhash.buf,osahash,oldhash.len)) { // Do not accept this password PRT("AddPassword: Password seems to be the same" ": please enter a different one"); passwd.hardreset(); pwdref.hardreset(); ranpwd.hardreset(); newpw = 1; } // Cleanup if (osahash) delete[] osahash; } } // // Calculate new hash, now if (passwd.length()) { // Get new hash char *nsahash = new char[(*KDFunLen)()]; // Encode first the hash with the salt int hlen = (*KDFun)(pwhash,pwhlen, salt.c_str(),salt.length(),nsahash,0); // Copy result in buf 2 ent.buf2.SetBuf(nsahash,hlen); // Cleanup if (nsahash) delete[] nsahash; } // // Cleanup if (pwhash) delete[] pwhash; // We are done return 1; } bool AddPassword(XrdSutPFEntry &ent, bool &newpw, const char *pwd) { // Prompt new password and save in hash form to entry ent // (if pwd is defined, take password from pwd). // If !newpw, the a pwd saved during a previous call is used, // if any. // Return 1 if ok, 0 otherwise. static XrdOucString pwdref; // // Prepare to get passwrod XrdOucString passwd = ""; if (newpw || !pwdref.length()) { newpw = 1; pwdref = ""; } // // If we are given a password, use it if (pwd && strlen(pwd) > 0) { PRT("AddPassword: using input password ("<ID(); buf += "puk: "; buf += ptag; buf += "\n"; int lpub = 0; char *pub = RefCip[i]->Public(lpub); if (pub) { buf += pub; buf += "\n"; delete[] pub; } buf += "epuk\n"; } buf += "\n"; buf += "*********************************************"; // // Write it to file // Now write the buffer to the stream while (write(fd, buf.c_str(), buf.length()) < 0 && errno == EINTR) errno = 0; // // Close file close (fd); // We are done return; } bool GetEntry(XrdSutPFile *ff, XrdOucString tag, XrdSutPFEntry &ent, bool &check) { // Get antry from file, checking force // Returns 1 if it exists and should not be updated // 0 otherwise int nr = ff->ReadEntry(tag.c_str(),ent); check = 0; if (nr > 0) { if (!Force) { PRT(" Entry for tag '"<Name()); PRT(" Details: "<@' and associated password // Make sure that the filename is defined if (PwdFile.length() <= 0) { PRT("ReadPasswd: file name undefined - do nothing"); return 0; } // // Open file in read mode FILE *fd = fopen(PwdFile.c_str(),"r"); if (fd == 0) { PRT("ReadPasswd: could not open file: "< 0) { tag += '@'; tag += host; tag += ':'; } // // Add srv ID, if any if (id.length() > 0) { tag += id; } // // Notify tag PRT("ReadPasswd: build tag: "<:_' // Make sure that the filename is defined if (PukFile.length() <= 0) { PRT("ReadPuk: file name undefined - do nothing"); return 0; } // // Open file in read mode FILE *fd = fopen(PukFile.c_str(),"r"); if (fd == 0) { PRT("ReadPuk: could not open file: "<AsBucket(); if (!bck[i]) continue; // // Count lout += (bck[i]->size + 2*sizeof(kXR_int32)); } // // Get the buffer char *bout = new char[lout]; if (!bout) { PRT("SavePuk: Cannot create output buffer"); close(fd); return 0; } // // Loop over ciphers to fill the buffer int lp = 0; for (i = 0; i < ncrypt; i++) { // // Make sure it is defined if (!CF[i] || !bck[i]) continue; // // The crypto ID first kXR_int32 id = CF[i]->ID(); memcpy(bout+lp,&id,sizeof(kXR_int32)); lp += sizeof(kXR_int32); // // The length second kXR_int32 lpuk = bck[i]->size; memcpy(bout+lp,&lpuk,sizeof(kXR_int32)); lp += sizeof(kXR_int32); // // Finally the content memcpy(bout+lp,bck[i]->buffer,lpuk); lp += lpuk; // // Cleanup delete bck[i]; bck[i] = 0; } delete[] bck; // // Write it to file // Now write the buffer to the stream while (write(fd, bout, lout) < 0 && errno == EINTR) errno = 0; PRT("SavePuk: "<= 0) { if (CF[i] && CF[i]->ID() == id) break; i--; } if (i < 0) { PRT("ReadPuk: warning: factory with ID "<< id << " not found"); delete bck; continue; } // Instantiate cipher from bucket RefCip[i] = CF[i]->Cipher(bck); if (!RefCip[i]) { PRT("ReadPuk: warning: could not instantiate cipher" " from bucket for factory "<Name()); } else { PRT("ReadPuk: instantiate cipher for factory "<Name()); } // Count good ciphers ncip++; delete bck; } // // Close file close (fd); PRT("ReadPuk: "<Cipher(0,0,0); if (!RefCip[i]) continue; // // Count success ncf++; } // We are done return ncf; } int LocateFactoryIndex(char *tag, int &id) { // Searches tag for "_" final strings // Extracts id and locate position in crypto array // // Locate factory ID XrdOucString sid(tag); sid.erase(0,sid.rfind('_')+1); id = atoi(sid.c_str()); int j = ncrypt - 1; while (j >= 0) { if (CF[j] && CF[j]->ID() == id) break; j--; } if (j < 0) PRT("// warning: factory with ID "<< id << " not found"); return j; } bool ExpPuk(const char *puk, bool read) { // Export public part of key contained in file 'puk'. The file // name can be absolute or relative to the standard 'genpuk' or // a date to be looked for in the genpuk directory. The public // key is exported in a file adding the extension ".export" // to 'puk'. If the file name is not defined the most recent // key in the standard genpuk directory is exported. // Return 0 in case of failure, 1 in case of success. // Read the keys in, if needed if (read) { // Standard genpuk dir XrdOucString genpukdir = Dir; genpukdir += GenPukRef; // Locate the file with the full key if (puk && strlen(puk) > 0) { // If not absolute, expand with respect to the standard genpuk dir if (puk[0] != '/') PukFile = genpukdir; PukFile += puk; } else { // Scan the standard genpuk to find the most recent key DIR *dir = opendir(genpukdir.c_str()); if (!dir) { PRT("ExpPuk: cannot open standard genpuk dir "<d_name, "puk.", 4)) continue; // Get the modification date XrdOucString fn = genpukdir; fn += ent->d_name; struct stat st; if (stat(fn.c_str(), &st) != 0) { PRT("ExpPuk: cannot stat "< latest) { PukFile = fn; latest = st.st_mtime; } } } // Read the keys in if (!ReadPuk()) { PRT("ExpPuk: problem reading the key in"); return 0; } } // Build the export file name XrdOucString expfile = PukFile; expfile += ".export"; PRT("ExpPuk: exporting key from file "<ID(); buf += "puk: "; buf += ptag; buf += "\n"; int lpub = 0; char *pub = RefCip[i]->Public(lpub); if (pub) { buf += pub; buf += "\n"; delete[] pub; } buf += "epuk\n"; } buf += "\n"; buf += "*********************************************"; // // Write it to file // Now write the buffer to the stream while (write(fd, buf.c_str(), buf.length()) < 0 && errno == EINTR) errno = 0; // // Close file close (fd); // We are done return 1; }