/******************************************************************************/
/* */
/* 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;
}