libzypp
10.5.0
|
00001 /*---------------------------------------------------------------------\ 00002 | ____ _ __ __ ___ | 00003 | |__ / \ / / . \ . \ | 00004 | / / \ V /| _/ _/ | 00005 | / /__ | | | | | | | 00006 | /_____||_| |_| |_| | 00007 | | 00008 \---------------------------------------------------------------------*/ 00013 #include <iostream> 00014 #include <fstream> 00015 00016 #include "zypp/base/Logger.h" 00017 #include "zypp/base/Gettext.h" 00018 #include "zypp/TmpPath.h" 00019 #include "zypp/KVMap.h" 00020 #include "zypp/media/Mount.h" 00021 #include "zypp/media/MediaUserAuth.h" 00022 #include "zypp/media/CredentialManager.h" 00023 #include "zypp/ZYppCallbacks.h" 00024 #include "zypp/ZConfig.h" 00025 00026 #include "zypp/media/MediaCIFS.h" 00027 00028 #include <sys/types.h> 00029 #include <sys/mount.h> 00030 #include <errno.h> 00031 #include <dirent.h> 00032 00033 using namespace std; 00034 00035 namespace zypp { 00036 namespace media { 00037 00038 /****************************************************************** 00039 ** 00040 ** 00041 ** FUNCTION NAME : getShare 00042 ** FUNCTION TYPE : inline Pathname 00043 ** 00044 ** Get the 1st path component (CIFS share name). 00045 */ 00046 inline string getShare( Pathname spath_r ) 00047 { 00048 if ( spath_r.empty() ) 00049 return string(); 00050 00051 string share( spath_r.absolutename().asString() ); 00052 string::size_type sep = share.find( "/", 1 ); 00053 if ( sep == string::npos ) 00054 share = share.erase( 0, 1 ); // nothing but the share name in spath_r 00055 else 00056 share = share.substr( 1, sep-1 ); 00057 00058 // deescape %2f in sharename 00059 while ( (sep = share.find( "%2f" )) != string::npos ) { 00060 share.replace( sep, 3, "/" ); 00061 } 00062 00063 return share; 00064 } 00065 00066 /****************************************************************** 00067 ** 00068 ** 00069 ** FUNCTION NAME : stripShare 00070 ** FUNCTION TYPE : inline Pathname 00071 ** 00072 ** Strip off the 1st path component (CIFS share name). 00073 */ 00074 inline Pathname stripShare( Pathname spath_r ) 00075 { 00076 if ( spath_r.empty() ) 00077 return Pathname(); 00078 00079 string striped( spath_r.absolutename().asString() ); 00080 string::size_type sep = striped.find( "/", 1 ); 00081 if ( sep == string::npos ) 00082 return "/"; // nothing but the share name in spath_r 00083 00084 return striped.substr( sep ); 00085 } 00086 00088 // 00089 // CLASS NAME : MediaCIFS 00090 // 00092 00094 // 00095 // 00096 // METHOD NAME : MediaCIFS::MediaCIFS 00097 // METHOD TYPE : Constructor 00098 // 00099 // DESCRIPTION : 00100 // 00101 MediaCIFS::MediaCIFS( const Url & url_r, 00102 const Pathname & attach_point_hint_r ) 00103 : MediaHandler( url_r, attach_point_hint_r, 00104 stripShare( url_r.getPathName() ), // urlpath WITHOUT share name at attachpoint 00105 false ) // does_download 00106 { 00107 MIL << "MediaCIFS::MediaCIFS(" << url_r << ", " << attach_point_hint_r << ")" << endl; 00108 } 00109 00111 // 00112 // 00113 // METHOD NAME : MediaCIFS::attachTo 00114 // METHOD TYPE : PMError 00129 void MediaCIFS::attachTo(bool next) 00130 { 00131 if(_url.getHost().empty()) 00132 ZYPP_THROW(MediaBadUrlEmptyHostException(_url)); 00133 if(next) 00134 ZYPP_THROW(MediaNotSupportedException(_url)); 00135 00136 string path = "//"; 00137 path += _url.getHost() + "/" + getShare( _url.getPathName() ); 00138 00139 MediaSourceRef media( new MediaSource( "cifs", path)); 00140 AttachedMedia ret( findAttachedMedia( media)); 00141 00142 if( ret.mediaSource && 00143 ret.attachPoint && 00144 !ret.attachPoint->empty()) 00145 { 00146 DBG << "Using a shared media " 00147 << ret.mediaSource->name 00148 << " attached on " 00149 << ret.attachPoint->path 00150 << endl; 00151 00152 removeAttachPoint(); 00153 setAttachPoint(ret.attachPoint); 00154 setMediaSource(ret.mediaSource); 00155 return; 00156 } 00157 00158 std::string mountpoint = attachPoint().asString(); 00159 if( !isUseableAttachPoint(attachPoint())) 00160 { 00161 mountpoint = createAttachPoint().asString(); 00162 if( mountpoint.empty()) 00163 ZYPP_THROW( MediaBadAttachPointException(url())); 00164 setAttachPoint( mountpoint, true); 00165 } 00166 00167 Mount mount; 00168 CredentialManager cm; 00169 00170 Mount::Options options( _url.getQueryParam("mountoptions") ); 00171 string username = _url.getUsername(); 00172 string password = _url.getPassword(); 00173 00174 if ( ! options.has( "rw" ) ) { 00175 options["ro"]; 00176 } 00177 00178 // look for a workgroup 00179 string workgroup = _url.getQueryParam("workgroup"); 00180 if ( workgroup.empty() ) 00181 workgroup = _url.getQueryParam("domain"); 00182 if ( !workgroup.empty() ) 00183 options["domain"] = workgroup; 00184 00185 // extract 'username', do not overwrite any _url.username 00186 00187 Mount::Options::iterator toEnv; 00188 toEnv = options.find("username"); 00189 if ( toEnv != options.end() ) { 00190 if ( username.empty() ) 00191 username = toEnv->second; 00192 options.erase( toEnv ); 00193 } 00194 00195 toEnv = options.find("user"); // actually cifs specific 00196 if ( toEnv != options.end() ) { 00197 if ( username.empty() ) 00198 username = toEnv->second; 00199 options.erase( toEnv ); 00200 } 00201 00202 // extract 'password', do not overwrite any _url.password 00203 00204 toEnv = options.find("password"); 00205 if ( toEnv != options.end() ) { 00206 if ( password.empty() ) 00207 password = toEnv->second; 00208 options.erase( toEnv ); 00209 } 00210 00211 toEnv = options.find("pass"); // actually cifs specific 00212 if ( toEnv != options.end() ) { 00213 if ( password.empty() ) 00214 password = toEnv->second; 00215 options.erase( toEnv ); 00216 } 00217 00218 if ( username.empty() || password.empty() ) 00219 { 00220 AuthData_Ptr c = cm.getCred(_url); 00221 if (c) 00222 { 00223 username = c->username(); 00224 password = c->password(); 00225 } 00226 } 00227 00228 bool firstTry = true; 00229 bool authRequired = false; 00230 AuthData authdata; 00231 do // repeat this while the mount returns "Permission denied" error 00232 { 00233 // get credentials from authenicate() 00234 if ( !firstTry ) 00235 { 00236 username = authdata.username(); 00237 password = authdata.password(); 00238 } 00239 00240 // pass 'username' and 'password' via environment 00241 Mount::Environment environment; 00242 if ( !username.empty() ) 00243 environment["USER"] = username; 00244 if ( !password.empty() ) 00245 environment["PASSWD"] = password; 00246 00248 // In case we need a tmpfile, credentials will remove 00249 // it in it's destructor after the mout call below. 00250 filesystem::TmpPath credentials; 00251 if ( !username.empty() || !password.empty() ) 00252 { 00253 filesystem::TmpFile tmp; 00254 ofstream outs( tmp.path().asString().c_str() ); 00255 outs << "username=" << username << endl; 00256 outs << "password=" << password << endl; 00257 outs.close(); 00258 00259 credentials = tmp; 00260 options["credentials"] = credentials.path().asString(); 00261 } 00262 else 00263 { 00264 // Use 'guest' option unless explicitly disabled (bnc #547354) 00265 if ( options.has( "noguest" ) ) 00266 options.erase( "noguest" ); 00267 else 00268 // prevent smbmount from asking for password 00269 // only add this option if 'credentials' is not used (bnc #560496) 00270 options["guest"]; 00271 } 00272 00273 // 00275 00276 try 00277 { 00278 mount.mount( path, mountpoint, "cifs", 00279 options.asString(), environment ); 00280 setMediaSource(media); 00281 break; 00282 } 00283 catch (const MediaMountException & e) 00284 { 00285 ZYPP_CAUGHT( e ); 00286 00287 if ( e.mountError() == "Permission denied" ) 00288 authRequired = authenticate( authdata, firstTry ); 00289 else 00290 ZYPP_RETHROW( e ); 00291 } 00292 00293 firstTry = false; 00294 } 00295 while ( authRequired ); 00296 00297 // wait for /etc/mtab update ... 00298 // (shouldn't be needed) 00299 int limit = 3; 00300 bool mountsucceeded; 00301 while( !(mountsucceeded=isAttached()) && --limit) 00302 sleep(1); 00303 00304 if ( !mountsucceeded ) 00305 { 00306 setMediaSource(MediaSourceRef()); 00307 try 00308 { 00309 mount.umount(attachPoint().asString()); 00310 } 00311 catch (const MediaException & excpt_r) 00312 { 00313 ZYPP_CAUGHT(excpt_r); 00314 } 00315 ZYPP_THROW(MediaMountException( 00316 "Unable to verify that the media was mounted", 00317 path, mountpoint 00318 )); 00319 } 00320 } 00321 00323 // 00324 // METHOD NAME : MediaCIFS::isAttached 00325 // METHOD TYPE : bool 00326 // 00327 // DESCRIPTION : Override check if media is attached. 00328 // 00329 bool 00330 MediaCIFS::isAttached() const 00331 { 00332 return checkAttached(true); 00333 } 00334 00336 // 00337 // 00338 // METHOD NAME : MediaCIFS::releaseFrom 00339 // METHOD TYPE : PMError 00340 // 00341 // DESCRIPTION : Asserted that media is attached. 00342 // 00343 void MediaCIFS::releaseFrom( const std::string & ejectDev ) 00344 { 00345 Mount mount; 00346 mount.umount(attachPoint().asString()); 00347 } 00348 00350 // 00351 // METHOD NAME : MediaCIFS::getFile 00352 // METHOD TYPE : PMError 00353 // 00354 // DESCRIPTION : Asserted that media is attached. 00355 // 00356 void MediaCIFS::getFile (const Pathname & filename) const 00357 { 00358 MediaHandler::getFile( filename ); 00359 } 00360 00362 // 00363 // METHOD NAME : MediaCIFS::getDir 00364 // METHOD TYPE : PMError 00365 // 00366 // DESCRIPTION : Asserted that media is attached. 00367 // 00368 void MediaCIFS::getDir( const Pathname & dirname, bool recurse_r ) const 00369 { 00370 MediaHandler::getDir( dirname, recurse_r ); 00371 } 00372 00374 // 00375 // 00376 // METHOD NAME : MediaCIFS::getDirInfo 00377 // METHOD TYPE : PMError 00378 // 00379 // DESCRIPTION : Asserted that media is attached and retlist is empty. 00380 // 00381 void MediaCIFS::getDirInfo( std::list<std::string> & retlist, 00382 const Pathname & dirname, bool dots ) const 00383 { 00384 MediaHandler::getDirInfo( retlist, dirname, dots ); 00385 } 00386 00388 // 00389 // 00390 // METHOD NAME : MediaCIFS::getDirInfo 00391 // METHOD TYPE : PMError 00392 // 00393 // DESCRIPTION : Asserted that media is attached and retlist is empty. 00394 // 00395 void MediaCIFS::getDirInfo( filesystem::DirContent & retlist, 00396 const Pathname & dirname, bool dots ) const 00397 { 00398 MediaHandler::getDirInfo( retlist, dirname, dots ); 00399 } 00400 00401 bool MediaCIFS::getDoesFileExist( const Pathname & filename ) const 00402 { 00403 return MediaHandler::getDoesFileExist( filename ); 00404 } 00405 00406 bool MediaCIFS::authenticate(AuthData & authdata, bool firstTry) const 00407 { 00409 CredentialManager cm(CredManagerOptions(ZConfig::instance().systemRoot())); 00410 00411 // get stored credentials 00412 AuthData_Ptr cmcred = cm.getCred(_url); 00413 00414 AuthData_Ptr smbcred; 00415 smbcred.reset(new AuthData()); 00416 callback::SendReport<AuthenticationReport> auth_report; 00417 00418 // preset the username if present in current url 00419 if (!_url.getUsername().empty() && firstTry) 00420 smbcred->setUsername(_url.getUsername()); 00421 // if CM has found some credentials, preset the username from there 00422 else if (cmcred) 00423 smbcred->setUsername(cmcred->username()); 00424 00425 // indicate we have no good credentials from CM 00426 cmcred.reset(); 00427 00428 string prompt_msg = str::form( 00430 _("Authentication required for '%s'"), _url.asString().c_str()); 00431 00432 // ask user 00433 if (auth_report->prompt(_url, prompt_msg, *smbcred)) 00434 { 00435 DBG << "callback answer: retry" << endl 00436 << "AuthData: " << *smbcred << endl; 00437 00438 if (smbcred->valid()) 00439 { 00440 cmcred = smbcred; 00441 // if (credentials->username() != _url.getUsername()) 00442 // _url.setUsername(credentials->username()); 00450 } 00451 } 00452 else 00453 DBG << "callback answer: cancel" << endl; 00454 00455 // set username and password 00456 if (cmcred) 00457 { 00458 authdata.setUsername(cmcred->username()); 00459 authdata.setPassword(cmcred->password()); 00460 00461 // save the credentials 00462 cmcred->setUrl(_url); 00463 cm.addCred(*cmcred); 00464 cm.save(); 00465 00466 return true; 00467 } 00468 00469 return false; 00470 } 00471 00472 00473 } // namespace media 00474 } // namespace zypp