Back to index

nordugrid-arc-nox  1.1.0~rc6
UsernameToken.cpp
Go to the documentation of this file.
00001 #ifdef HAVE_CONFIG_H
00002 #include <config.h>
00003 #endif
00004 
00005 #ifdef WIN32
00006 #include <arc/win32.h>
00007 #endif
00008 #include <stdlib.h>
00009 #include <sys/time.h>
00010 
00011 #include <string>
00012 #include <sstream>
00013 #include <fstream>
00014 #include <iostream>
00015 //#include <iomanip>
00016 
00017 #include <openssl/evp.h>
00018 #include <openssl/sha.h>
00019 #include <openssl/rand.h>
00020 #ifdef CHARSET_EBCDIC
00021 #include <openssl/ebcdic.h>
00022 #endif 
00023 
00024 #include <arc/DateTime.h>
00025 #include <arc/Base64.h>
00026 #include <arc/StringConv.h>
00027 #include "UsernameToken.h"
00028 
00029 namespace Arc {
00030 
00031 #define WSSE_NAMESPACE   "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" 
00032 #define WSSE11_NAMESPACE "http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd"
00033 #define WSU_NAMESPACE    "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
00034 
00035 #define USERNAMETOKEN_BASE_URL "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0"
00036 #define USENAME_TOKEN "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#UsernameToken"
00037 #define PASSWORD_TEXT "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText"
00038 #define PASSWORD_DIGEST "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest"
00039 
00040 #define PASS_MIN_LENGTH 4
00041 static bool get_password(std::string& password, bool verify) {
00042   char pwd[1024];
00043   int len = sizeof(pwd);
00044   int j, r;
00045   char prompt[128];
00046   for(;;) {
00047     snprintf(prompt, sizeof(prompt), "Enter password for Username Token: "); 
00048     r = EVP_read_pw_string(pwd, len, prompt, 0);
00049     if(r != 0) {
00050       std::cerr<<"Failed to read read input password"<<std::endl;
00051       memset(pwd,0,(unsigned int)len);
00052       return false;
00053     }
00054     j = strlen(pwd);
00055     if(j < PASS_MIN_LENGTH) {
00056       std::cerr<<"Input phrase is too short (must be at least "<<PASS_MIN_LENGTH<<" chars)"<<std::endl;
00057     }
00058     else if(!verify) {
00059       password=pwd;
00060       return true;
00061     } 
00062     else {
00063       snprintf(prompt, sizeof(prompt), "Enter password again: ");
00064       char buf[1024];
00065       r = EVP_read_pw_string(buf, len, prompt, 0);
00066       if(r != 0) {
00067         std::cerr<<"Failed to read input password"<<std::endl;
00068         return false;
00069       }
00070       if(strcmp(pwd, buf) != 0) {
00071         std::cerr<<"Two input passwords do not match, try again"<<std::endl;    
00072       }
00073       else {
00074         password=pwd;
00075         return true;
00076       }
00077     }//end else
00078   }//end for  
00079   return false;
00080 }
00081 
00082 static bool get_username(std::string& user) {
00083   int r;
00084   char buf[256];
00085   setvbuf(stdin, (char *)NULL, _IONBF, 0);
00086   std::cout<<"Enter username for Username Token: ";
00087   if (!(fgets(buf, 256, stdin))) {
00088     std::cerr<<"Failed to read username"<<std::endl;
00089     return false;
00090   }
00091   r = strlen(buf);
00092   if (buf[r-1] == '\n') {
00093     buf[r-1] = '\0';
00094     r--;
00095   }
00096   user = buf;
00097   return (r == 0);  
00098 }
00099 
00100 //Get the password from a local file; it could be extended to get password from some database
00101 static std::string get_password_from_file(std::istream& f, const std::string& username) {
00102   size_t left, right, found, found1, found2;
00103   std::string str, user, passwd;
00104   while (!f.eof() && !f.fail()) {
00105     std::getline(f, str);
00106 
00107     left = str.find_first_not_of(" ");
00108     found1 = str.find_first_of(" ", left);
00109     found2 = str.find_first_of(",", left);
00110     right = found1 <= found2 ? found1 : found2;
00111     if(right!=std::string::npos) {
00112       user = str.substr(left, right - left);
00113       if(user.compare(username)!=0){continue;}
00114 
00115       found = str.find_first_not_of(" ", right);
00116       left = found;
00117       found = str.find_first_not_of(",", left);
00118       left = found;
00119       found = str.find_first_not_of(" ", left);
00120       left = found;
00121 
00122       found1 = str.find_first_of(" ", left);
00123       found2 = str.find_first_of("\n", left);
00124       right = found1 <= found2 ? found1 : found2;
00125       passwd = str.substr(left, right - left);
00126       break;
00127     }
00128   }
00129   //f.close();
00130   return passwd;  
00131 }
00132 
00133 static XMLNode get_node(XMLNode& parent,const char* name) {
00134   XMLNode n = parent[name];
00135   if(!n) n=parent.NewChild(name);
00136   return n;
00137 }
00138 
00139 static std::string get_nonce() {
00140   unsigned char buf[16];
00141   int i = RAND_pseudo_bytes(buf, 16);
00142   if (i < 0) {
00143     std::cout<<"Something wrong with random generator"<<std::endl;
00144   }
00145   char encoded [25];
00146   Base64::encode(encoded ,(const char*)buf,16);
00147   
00148   std::string ret(encoded);
00149   //std::cout<<"Generated nonce: "<<ret<<std::endl;
00150   return ret;
00151 }
00152 
00153 static std::string get_salt(bool mac) {
00154   unsigned char buf[16];
00155   int i = RAND_pseudo_bytes(buf, 16);
00156   if (i < 0) {
00157     std::cout<<"Something wrong with random generator"<<std::endl;
00158   }
00159 
00160   if(mac){buf[15] = 0x01;}
00161   else {buf[15] = 0x02;}
00162 
00163   char encoded [25];
00164   Base64::encode(encoded ,(const char*)buf,16);
00165 
00166   std::string ret(encoded);
00167   //std::cout<<"Generated salt: "<<ret<<std::endl;
00168   return ret;
00169 }
00170 
00171 static std::string digest_password(const std::string& nonce, const std::string& created, const std::string& passwd) {
00172   EVP_MD_CTX mdctx;       
00173   unsigned char md_value[SHA_DIGEST_LENGTH];
00174   unsigned int md_len;
00175 
00176   char plain[1024];
00177   Base64::decode(plain, nonce.c_str());
00178   std::string todigest(plain);
00179 
00180   //UTF-8?
00181   todigest.append(created);
00182   todigest.append(passwd);
00183 
00184   //std::cout<<"To digest:"<<todigest<<std::endl;
00185     
00186   EVP_MD_CTX_init(&mdctx);
00187   EVP_DigestInit_ex(&mdctx, EVP_sha1(), NULL);
00188   EVP_DigestUpdate(&mdctx, todigest.c_str(), todigest.length());
00189   EVP_DigestFinal_ex(&mdctx, md_value, &md_len);
00190   EVP_MD_CTX_cleanup(&mdctx);
00191 
00192   char encoded [256];
00193   Base64::encode(encoded, (const char*)md_value, SHA_DIGEST_LENGTH);
00194   std::string ret(encoded);
00195 
00196   return ret;
00197 }
00198 
00199 static std::string generate_derivedkey(const std::string& password, const std::string& salt, int iteration ) {
00200   EVP_MD_CTX mdctx;
00201   unsigned char md_value[SHA_DIGEST_LENGTH];
00202   unsigned int md_len;
00203   int i;
00204 
00205   std::string todigest(password);
00206   todigest.append(salt);
00207 
00208   //std::cout<<"To digest:"<<todigest<<" "<<iteration<<std::endl;
00209 
00210   EVP_MD_CTX_init(&mdctx);
00211   for(i = 0; i< iteration; i++) {
00212     EVP_DigestInit_ex(&mdctx, EVP_sha1(), NULL);
00213     EVP_DigestUpdate(&mdctx, todigest.c_str(), todigest.length());
00214     memset(md_value, 0, SHA_DIGEST_LENGTH);
00215     EVP_DigestFinal_ex(&mdctx, md_value, &md_len);
00216     todigest.assign((char*)md_value,md_len);
00217   }
00218 
00219   EVP_MD_CTX_cleanup(&mdctx);
00220 
00221   return todigest;
00222 }
00223 
00224 bool UsernameToken::Check(SOAPEnvelope& soap) {
00225   XMLNode header = soap.Header();
00226   if(header.NamespacePrefix(WSSE_NAMESPACE).empty()){ 
00227     std::cerr<<"No wsse namespace in SOAP Header"<<std::endl;
00228     return false;
00229   }
00230   XMLNode wsse;
00231   if(!(wsse=header["wsse:Security"])) {
00232     std::cerr<<"No Security element in SOAP Header"<<std::endl;
00233     return false;
00234   };
00235   if(!wsse["wsse:UsernameToken"]) {
00236     std::cerr<<"No UsernameToken element in SOAP Header"<<std::endl;
00237     return false;
00238   };
00239   return true;
00240 }
00241 
00242 UsernameToken::operator bool(void) {
00243   return (bool)header_;
00244 }
00245 
00246 std::string UsernameToken::Username(void) {
00247   return username_;
00248 }
00249 
00250 UsernameToken::UsernameToken(SOAPEnvelope& soap) {
00251   if(!Check(soap)){
00252     return;    
00253   }
00254 
00255   header_=soap.Header();
00256 
00257   // Apply predefined namespace prefix
00258   NS ns;
00259   ns["wsse"]=WSSE_NAMESPACE;
00260   ns["wsse11"]=WSSE11_NAMESPACE;
00261   ns["wsu"]=WSU_NAMESPACE;
00262   header_.Namespaces(ns);
00263 
00264   XMLNode ut = header_["wsse:Security"]["wsse:UsernameToken"];   
00265   bool has_password = (bool)ut["wsse:Password"];
00266   username_ = (std::string)(ut["wsse:Username"]);
00267   password_ = (std::string)(ut["wsse:Password"]);
00268   nonce_    = (std::string)(ut["wsse:Nonce"]);
00269   created_  = (std::string)(ut["wsu:Created"]);
00270   salt_     = (std::string)(ut["wsse11:Salt"]);
00271   if(!ut["wsse11:Iteration"]) {
00272     iteration_=1000; // Default iteration value
00273   } else {
00274     if(!stringto(ut["wsse11:Iteration"],iteration_)) {
00275       std::cerr<<"Wrong number of iterations"<<std::endl;
00276       header_=XMLNode();
00277       return;
00278     }
00279   }
00280 
00281   //std::cout<<"Username: "<<username_<<" Password: "<<password_<<" Nonce: "<<nonce_<<" Created: "<<created_<<std::endl;
00282 
00283   if(username_.empty()) {
00284     std::cerr<<"Element wsse:Username is missing or empty"<<std::endl;
00285     header_=XMLNode();
00286     return;
00287   }
00288   if(has_password) {  //PasswordText or PasswordDigest
00289     if(!salt_.empty()) {
00290       std::cerr<<"Can not have wsse11:Salt and wsse:Password at the same time"<<std::endl;
00291       header_=XMLNode();
00292       return;
00293     }
00294     passwdtype_ = (std::string)((ut["wsse:Password"]).Attribute("Type"));
00295     if(passwdtype_.empty() || (passwdtype_ == PASSWORD_TEXT)) {
00296     }
00297     else if(passwdtype_ == PASSWORD_DIGEST) {
00298       if(nonce_.empty() || created_.empty()) {
00299         std::cerr<<"Digest password type requires wsse::Nonce and wsu::Created"<<std::endl;
00300         return;
00301       }
00302     }
00303     else {
00304       std::cerr<<"Unsupported password type found: "<<passwdtype_<<std::endl;
00305       header_=XMLNode();
00306       return;
00307     }
00308   }
00309   else {  //Key Derivation
00310     if(salt_.empty()) {
00311       std::cerr<<"Key Derivation feature requires wsse11::Salt "<<std::endl;
00312       header_=XMLNode();
00313       return;
00314     }
00315   }
00316 } 
00317 
00318 
00319 bool UsernameToken::Authenticate(std::istream& password,std::string& derived_key) {
00320   //Get the original password for this user
00321   std::string passwd_text = get_password_from_file(password, username_);
00322   //std::cout<<"Password get from local file -----"<<passwd_text<<std::endl;
00323   return Authenticate(passwd_text,derived_key);
00324 }
00325 
00326 bool UsernameToken::Authenticate(const std::string& password,std::string& derived_key) {
00327   if(salt_.empty()) { // Password
00328     if(!nonce_.empty()) { // PasswordDigest
00329       std::string passwd_digest = digest_password(nonce_, created_ , password);
00330       if(passwd_digest != password_) {
00331         std::cerr<<"Generated Password Digest does not match that in wsse:Password: "<<passwd_digest<<" != "<<password_<<std::endl;
00332         return false;
00333       }
00334     }
00335     else {
00336       if(password != password_) {
00337         std::cerr<<"provide Password does not match that in wsse:Password: "<<password<<" != "<<password_<<std::endl;
00338         return false;
00339       }
00340     }
00341   }
00342   else { //Derive the key
00343     derived_key = generate_derivedkey(password, salt_, iteration_);
00344     if(derived_key.empty()) return false;
00345     //std::cout<<"Derived Key: "<<derived_key<<std::endl;
00346   } 
00347   return true;
00348 }
00349 
00350 
00351 UsernameToken::UsernameToken(SOAPEnvelope& soap, const std::string& username, const std::string& password,const std::string& uid, PasswordType pwdtype) {
00352   header_=soap.Header();
00353   uid_ = uid;
00354   iteration_ = 0;
00355   
00356   //Get the username
00357   username_=username;
00358   if(username_.empty()) get_username(username_);
00359 
00360   //Get the password  
00361   password_=password;
00362   if(password_.empty()) get_password(password_,false);
00363 
00364   // Apply predefined namespace prefix
00365   NS ns;
00366   ns["wsse"]=WSSE_NAMESPACE;
00367   ns["wsse11"]=WSSE11_NAMESPACE;
00368   ns["wsu"]=WSU_NAMESPACE;
00369   header_.Namespaces(ns);
00370 
00371   //Check the arguments
00372   if(username_.empty() || password_.empty()) {
00373     std::cerr<<"Username and Password should not be empty"<<std::endl;
00374     header_=XMLNode();
00375     return;
00376   }
00377 
00378   // Insert the related elements
00379   XMLNode wsse = get_node(header_,"wsse:Security");
00380   XMLNode ut = get_node(wsse, "wsse:UsernameToken");
00381   if(!uid_.empty()) {
00382     ut.NewAttribute("wsu:Id") = uid_;
00383   }
00384   get_node(ut, "wsse:Username") = username_;
00385   XMLNode passwd_node = get_node(ut, "wsse:Password");
00386 
00387   if(pwdtype == PasswordText) {
00388     passwd_node.NewAttribute("Type") = passwdtype_ = PASSWORD_TEXT;
00389     passwd_node = password_;
00390   }
00391   else if(pwdtype == PasswordDigest) {
00392     passwd_node.NewAttribute("Type") = passwdtype_ = PASSWORD_DIGEST; 
00393     nonce_ = get_nonce();
00394     get_node(ut, "wsse:Nonce") = nonce_;
00395     created_ = Time().str(UTCTime);
00396     get_node(ut, "wsu:Created") = created_;
00397 
00398     //std::cout<<"nonce: "<<nonce_<<"createdtime: "<<created_<<"password: "<<password_<<std::endl;
00399 
00400     std::string password_digest = digest_password(nonce_, created_, password_);
00401     passwd_node = password_digest;
00402   }
00403   else {
00404     std::cerr<<"Unsupported password type requested"<<std::endl;
00405     header_=XMLNode();
00406     return;
00407   }
00408 }
00409 
00410 UsernameToken::UsernameToken(SOAPEnvelope& soap, const std::string& username, const std::string& uid, bool mac, int iteration) {
00411   header_=soap.Header();
00412   uid_ = uid;
00413   iteration_ = iteration;
00414 
00415   //Get the username
00416   username_=username;
00417   if(username_.empty()) get_username(username_);
00418 
00419   salt_ = get_salt(mac);
00420 
00421   // Apply predefined namespace prefix
00422   NS ns;
00423   ns["wsse"]=WSSE_NAMESPACE;
00424   ns["wsse11"]=WSSE11_NAMESPACE;
00425   ns["wsu"]=WSU_NAMESPACE;
00426   header_.Namespaces(ns);
00427 
00428   //Check the arguments
00429   if(username_.empty() || salt_.empty()) {
00430     std::cerr<<"Username and Salt should not be empty"<<std::endl;
00431     header_=XMLNode();
00432     return;
00433   }
00434 
00435   // Insert the related elements
00436   XMLNode wsse = get_node(header_,"wsse:Security");
00437   XMLNode ut = get_node(wsse, "wsse:UsernameToken");
00438   if(!uid_.empty()) {
00439     ut.NewAttribute("wsse:Id") = uid_;
00440   }
00441   get_node(ut, "wsse:Username") = username_;
00442   get_node(ut, "wsse11:Salt") = salt_;
00443   get_node(ut, "wsse11:Iteration") = tostring(iteration);
00444 }
00445 
00446 } // namespace Arc
00447