00001 /* 00002 Copyright 2007 Erez Bibi (erezbibi@users.sourceforge.net) 00003 This file is part of Beesnest. 00004 00005 Beesnest is free software; you can redistribute it and/or modify 00006 it under the terms of the GNU General Public License as published by 00007 the Free Software Foundation; either version 2 of the License, or 00008 (at your option) any later version. 00009 00010 Beesnest is distributed in the hope that it will be useful, 00011 but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00013 GNU General Public License for more details. 00014 00015 You should have received a copy of the GNU General Public License 00016 along with Beesnest; if not, write to the Free Software 00017 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 00018 */ 00019 00020 /* DcLoginManager.h: interface for the CdcLoginManager class. */ 00021 00022 #ifndef __CDC_LOGIN_MANAGER 00023 #define __CDC_LOGIN_MANAGER 00024 00025 #include "DcGlobals.h" 00026 #include "DcRequest.h" 00027 #include "DcResponse.h" 00028 #include "DcHttpTime.h" 00029 #include "DcUserStatus.h" 00030 #include _GET_INCLUDE_PATH (DcCrypto.h) 00031 #include _GET_INCLUDE_PATH (DcMutex.h) 00032 #include _GET_INCLUDE_PATH (DcSemaphore.h) 00033 #include _GET_INCLUDE_PATH (DcThread.h) 00034 00035 typedef CdcString t_code_type; /* The one-time random code type. */ 00036 00037 /* Names... */ 00038 /* For built-in after first login. */ 00039 #define DC_USER_ID "BN_UserID" /* User ID. */ 00040 #define DC_USER_CODE "BN_RndCode" /* User one-time random code. */ 00041 #define DC_LOGIN_MSG "BN_LoginMsg" /* Friendly login message. */ 00042 00043 /* For challenge response - built-in or DAA. */ 00044 #define DC_USER_NAME "BN_Username" /* User Name. */ 00045 #define DC_URL "BN_OrigURL" /* Original requested URL. */ 00046 #define DC_REALM "BN_Realm" /* Group of protection. */ 00047 #define DC_NONCE "BN_Nonce" /* Server nonce. */ 00048 #define DC_OPAQUE "BN_Opaque" /* Server other random code. */ 00049 #define DC_CNONCE "BN_Cnonce" /* Client nonce. */ 00050 #define DC_NC "BN_NC" /* Nonce Counter. */ 00051 #define DC_RESPONSE "BN_Response" /* Client Response. */ 00052 00053 #define DC_LOGOUT "BN_Logout" /* user set it to "yes" in order to logout. */ 00054 00055 /* Return valuse from the login manager. */ 00056 #define DC_LM_NONE 0 /* no access restriction. */ 00057 #define DC_LM_OK 1 /* access is granted. */ 00058 #define DC_LM_SEND 2 /* need to send other (login/deny) page to user. */ 00059 00060 #define DC_CHL_STL_ADD 2 /* Wait a few more sec before delete stale challenge. */ 00061 00062 /** 00063 * The Log-in Manager class is a <b>static</b> class that deals with the login of 00064 * users to the system. The logging process can be done in two ways. One is the 00065 * RFC 2617 Digest Access Authentication (DAA) login process, and the other is 00066 * a server built-in login system. In addition both ways can be combined. <br> 00067 * A note should be made that the built-in login system is secure just if it is 00068 * working with SSL (or some other) encryption of the communication between the 00069 * server and the client. For full discussion of the login system see the 00070 * Login System document. 00071 * <p> 00072 * The server has a list of protected folders or files, in the Login-Manager 00073 * configuration part of the configuration files. Every file or folder that has 00074 * been set to be protected, belongs to some group of users. <br> 00075 * The groups list and their users are also listed in this section of the 00076 * configuration files (as a tree). Each group is an entry in the configuration 00077 * file, and under this entry you should write the users that belong to the 00078 * group or other groups names. <br> 00079 * The names of the login page, access-denied page and the name of the file 00080 * of user-names and passwords, are also set in this configuration section. 00081 * <p> 00082 * The Digest Access Authentication (DAA): In every request (or include) of a 00083 * protected file, the server will look for the "Authorization" header in the 00084 * request. If it finds this header it will test it against the user password. 00085 * If the server does not find this header it will send 401 response with a 00086 * WWW-Authenticate header that includes all the needed data for the client to 00087 * log in (the challenge). If the server did find the Authorization header, but 00088 * the challenge for this response was too old (stale) it will add a 00089 * "stale=true" to the challenge, so the user agent does not need to ask for 00090 * the password again. See RFC 2617 for full details. 00091 * <p> 00092 * The built-in login process starts in a similar way. The server send the exact 00093 * same challenge to the client, not in a header of 401 response, but embedded 00094 * in a special login HTML web page. the user enter his user-name and password 00095 * to an HTML form, then a (Javascript) calculation of the response should be 00096 * made on the client side, and the the same response of a DAA process should be 00097 * sent back as HTML request variables. <br> 00098 * After the first log-in the server will maintain one-time random code for this 00099 * user. This code will go back and forth between the server and the client 00100 * (different code each time), and as long as the client knows the correct code, 00101 * this user stays logged in. This process is what needs SSL for full security, 00102 * as the random code is passed in the open. (With the random code the server 00103 * sends the user an ID as well, so it can keep track of more then one user at 00104 * one time.<br> 00105 * Note that this login process require the use of some scripting language to 00106 * embed the challenge parameters in the HTML page. 00107 * <p> 00108 * There is an option to combine the two methods, and use the DAA process in the 00109 * beginning (so the user get the nice message box from the browser), but then 00110 * use the one-time code method. This will save the need of a login page with 00111 * complected Javascript, but will be more efficient than the full DAA process. 00112 * <p> 00113 * The way to tell the server which login method to use is by setting the name 00114 * (and path) of the login page in the Login-Manager configuration section. if 00115 * this file is missing the server will use DAA. <br> 00116 * Some other configuration options are: <br> 00117 * The Auto-Cookie parameter, if sets to "yes" will tell the server to insert 00118 * the one-time code and user ID to a cookie response header, every time they 00119 * exists, without the use of some scripting language. The Stale parameter will 00120 * set the time in seconds for a challenge to be valid and the Expire parameter 00121 * will set the time for a deletion of a challenge if it is not get any recent 00122 * response. In addition there should be a name (and path) of a deny page, if a 00123 * user is logged in but cannot see a certain page because he is not belong to 00124 * the correct group. If an access to a page is denied, the class will change 00125 * the page to the "Access Denied" page, You can still transfer the User Code 00126 * and User ID in this page so the user will not be logged out. 00127 * <p> 00128 * This class starts a lower priority thread to keep the challenges list clean 00129 * of old challenges. It signals this thread to scan the list after marking any 00130 * challenge for deletion, and after logging out a user (see somewhat long 00131 * explanation in the DcClean function). 00132 * 00133 * <p> 00134 * @author Erez Bibi 00135 * @version 2.0 00136 */ 00137 class CdcLoginManager 00138 { 00139 private: 00140 /** 00141 * This is basically a struct to hold the challenges this class sends to 00142 * users. The challenge is standard Digest Access Authentication challenge. 00143 * See RFC 2617. 00144 */ 00145 class CdcDaaChallenge 00146 { 00147 public: 00148 CdcString sRealm; /* Identify the group this challenge is for. */ 00149 CdcString sNonce; /* Nonce For this challenge. */ 00150 CdcString sOpaque; /* This string just goes to the client and back as is. */ 00151 int nNC; /* Counter for the response (prevent replay attack). */ 00152 CdcHttpTime oBuildTime; /* The time when this challenge was created. */ 00153 bool isForDelete; /* Flag to mark this challenge for deletion 00154 (but we can still use it for a while). */ 00155 bool isCanceld; /* This is another flag to mark challenge as void. */ 00156 }; 00157 00158 /** 00159 * This is a struct to hold the response we got from the user. 00160 * The response is standard Digest Access Authentication response. 00161 * See RFC 2617. 00162 */ 00163 class CdcDaaResponse 00164 { 00165 public: 00166 CdcString sUsername; /* The username. */ 00167 CdcString sRealm; /* Identify the group this response is for. */ 00168 CdcString sNonce; /* Nonce Form the challenge. */ 00169 CdcString sOpaque; /* This string that get back as is. */ 00170 CdcString sURL; /* The requested URL. */ 00171 CdcString sMethod; /* The request method. */ 00172 CdcString sQOP; /* Quality of Protection. */ 00173 CdcString sCnonce; /* Client nonce. */ 00174 CdcString sResponse; /* The response. */ 00175 CdcString sNC; /* Response counter as string */ 00176 }; 00177 00178 /** 00179 * CdcUser holds information of one user in the system. Its holds the 00180 * user-name, password, random code and a pointer to the user status object. 00181 * The class can write itself to a binary file, and create itself from a file. 00182 */ 00183 class CdcUser 00184 { 00185 private: 00186 /** The user name. */ 00187 CdcString sUsername; 00188 /** The user password. */ 00189 CdcString sPassword; 00190 /** One time random code (for the built in login system). */ 00191 t_code_type nCode; 00192 /** The last used code (for multithread requests) . */ 00193 t_code_type nLastCode; 00194 /** Pointer to the user status object. */ 00195 CdcUserStatus oStatus; 00196 /** True when the user is logged in. */ 00197 bool isLogedin; 00198 /** Mutex for this user. */ 00199 CdcMutex oMutex; 00200 /** The IP this user logged in from. */ 00201 CdcString sLoginIP; 00202 /** The time of last good (random code) login. */ 00203 CdcHttpTime tLastLogin; 00204 00205 public: 00206 /** Direct constructor and default constructor. */ 00207 CdcUser (const CdcString& name = EMPTY_STR, 00208 const CdcString& pass = EMPTY_STR) 00209 : sUsername (name), sPassword (pass), nCode (EMPTY_STR), 00210 isLogedin (false) {} 00211 00212 /** Constructor from a file. */ 00213 CdcUser (ifstream& inf); 00214 00215 /** Write object to a file. */ 00216 bool WriteToFile (ofstream& outf); 00217 00218 /* Sets and gets functions. */ 00219 CdcString GetUsername () 00220 { oMutex.Lock (); CdcString temp = sUsername; oMutex.Unlock (); return temp;} 00221 00222 void SetUsername (const CdcString& name) 00223 { oMutex.Lock (); sUsername = name; oMutex.Unlock (); } 00224 00225 CdcString GetPassword () 00226 { oMutex.Lock (); CdcString temp = sPassword; oMutex.Unlock (); return temp;} 00227 00228 void SetPassword (const CdcString& pass) 00229 { oMutex.Lock (); sPassword = pass; oMutex.Unlock (); } 00230 00231 CdcUserStatus* GetStatusObject () 00232 { oMutex.Lock (); CdcUserStatus* temp = &oStatus; oMutex.Unlock (); return temp;} 00233 00234 //void SetStatusObject (CdcUserStatus* pointer) 00235 //{ oMutex.Lock (); if (pStatus) delete pStatus; pStatus = pointer; oMutex.Unlock (); } 00236 00237 t_code_type GetCode () 00238 { oMutex.Lock (); t_code_type temp = nCode; oMutex.Unlock (); return temp;} 00239 00240 t_code_type GetLastCode () 00241 { oMutex.Lock (); t_code_type temp = nLastCode; oMutex.Unlock (); return temp;} 00242 00243 void SetCode (t_code_type code) 00244 { oMutex.Lock (); nLastCode = nCode; nCode = code; 00245 tLastLogin = CdcHttpTime::GetCurrentTime (); oMutex.Unlock (); } 00246 00247 CdcHttpTime GetLastLoginTime () 00248 { oMutex.Lock (); CdcHttpTime temp = tLastLogin; oMutex.Unlock (); return temp;} 00249 00250 bool IsLogedin () 00251 { oMutex.Lock (); bool temp = isLogedin; oMutex.Unlock (); return temp;} 00252 00253 void Logedin (bool yes) 00254 { oMutex.Lock (); isLogedin = yes; oMutex.Unlock (); } 00255 00256 void SetIP (const CdcString& ip) 00257 { oMutex.Lock (); sLoginIP = ip; oMutex.Unlock (); } 00258 00259 CdcString GetIP () 00260 { oMutex.Lock (); CdcString temp = sLoginIP; oMutex.Unlock (); return temp;} 00261 }; 00262 00263 00264 typedef vector <CdcUser*> t_users_vector; 00265 typedef map <CdcString, CdcDaaChallenge*> t_challenges_map; 00266 00267 00268 /** The vector of users. */ 00269 static t_users_vector vUsers; 00270 00271 /** The map of sent challenges. */ 00272 static t_challenges_map mChallenges; 00273 00274 /** The Mutex for this map. */ 00275 static CdcMutex oMapMutex; 00276 00277 /** The name of the login HTML file (const). */ 00278 static CdcString sLoginFileName; 00279 00280 /** The name of the deny access HTML file (const).*/ 00281 static CdcString sDenyFileName; 00282 00283 /** True if the login page is empty - using DAA (const). */ 00284 static bool bBuiltInLogin; 00285 00286 /** True if need to insert ID and Random Code cookie automatically (const). */ 00287 static bool bAutoCookie; 00288 00289 /** Time limit for a nonce to become stale (const). */ 00290 static int nStaleNonce; 00291 00292 /** Time limit for a used nonce to expire (const). */ 00293 static int nExpireNonce; 00294 00295 /** Time that an old one-time-code is still good, for multithread request of 00296 protected resources (from the same page). */ 00297 static int nOldCodeValid; 00298 00299 /** This object holds the cleaning thread. */ 00300 static CdcThread oCleanThread; 00301 /** Semaphore to trigger the cleaning thread. */ 00302 static CdcSemaphore oStartThread; 00303 /** Flag for the thread to exit. */ 00304 static bool isCleanThreadGo; 00305 /** Counter of taken challenges. */ 00306 static volatile int nSomeChalTaken; 00307 /* See explanation for these 4 vars in DcClean function. */ 00308 00309 /** There is no constructor (static class). */ 00310 CdcLoginManager () {} 00311 00312 /** 00313 * Clean stale, void and old challenges. It is been called from DcClean that 00314 * runs in a separate thread. See DcClean for full explanation. 00315 */ 00316 static void Clean (); 00317 00318 /** 00319 * Make the operation to set a user as logged in. It inserts the random code 00320 * and user ID as variables in the request object if using the built-in 00321 * method. It inserts these variables as cookies if the Auto Cookies is set 00322 * (even if we are using DAA). 00323 * <p> 00324 * @param id The ID of the user to log in (index in the vector). 00325 * @param req Reference to the request object. 00326 * @param res Reference to the response object. 00327 */ 00328 static void LoginUser (int id, CdcRequest& req, CdcResponse& res); 00329 00330 /** 00331 * Insert a new random code to a cookie or request variable, if we are using 00332 * random codes. In addition this sets the user ID as a cookie or request 00333 * variable. Note that in some cases setting the user ID is redundant. 00334 * <p> 00335 * @param id The ID of the user to log in (index in the vector). 00336 * @param req Reference to the request object. 00337 * @param res Reference to the response object. 00338 */ 00339 static void SetRandCode (int id, CdcRequest& req, CdcResponse& res); 00340 00341 /** 00342 * Log out a user, cleans its object and erases login variables from the 00343 * request. If Auto Cookies is set, it erases the login cookies as well. 00344 * <p> 00345 * @param req Reference to the request object. 00346 * @param res Reference to the response object. 00347 */ 00348 static void LogoutUser (CdcRequest& req, CdcResponse& res); 00349 00350 /** 00351 * This function checks the response arrived from the client. 00352 * This is the function that implements the Digest Access Authentication 00353 * algorithm. I use it in both login methods. For the built-in just once, 00354 * when the user logs in, for DAA I call this function before sending any 00355 * protected page. See RFC 2617. 00356 * <p> 00357 * @param id The ID of this user. 00358 * @param DaaRes Reference to a Digest access authentication response. 00359 * @param stale Reference to bool, set to True if DAA response is stale. 00360 * @param wrong Reference to bool, set to True if Username/Password is wrong. 00361 * @return (bool) True if the response is correct. 00362 */ 00363 static bool CheckResponse (int id, const CdcDaaResponse& DaaRes, 00364 bool& stale, bool& wrong); 00365 00366 /** 00367 * This function checks the one time random code from the client (built-in 00368 * method) 00369 * <p> 00370 * @param id The ID of this user. 00371 * @param req Reference to the request object. 00372 * @param res Reference to the response object. 00373 * @return (bool) True if the code is correct. 00374 */ 00375 static bool CheckCode (int id, CdcRequest& req, CdcResponse& res); 00376 00377 /** 00378 * This function checks if a user is under a group. It checks it by asking 00379 * the parameters class. 00380 * <p> 00381 * @param The ID of the user (place in the vector). 00382 * @param The name of the group the user should be in. 00383 * @return (bool) True if the user is in this group. 00384 */ 00385 static bool CheckGroupAccess (int id, const CdcString& group); 00386 00387 /** 00388 * This function prepares a login page. First it generates a new challenge. 00389 * Then it either just inserts the challenge variables to the request 00390 * object variables map, and from there they will go to the login page 00391 * using a script. Or if we are using DAA, it adds an WWW-Authenticate 00392 * header to the response, and changes the response value to 401. In any way, 00393 * the ConncetionThread class will clear the previous buffer to send to the 00394 * client, and either send an empty buffer (DAA) or send the login page. 00395 * <p> 00396 * @param req Reference to the request object. 00397 * @param res Reference to the response object. 00398 * @param stale True if DAA nonce was stale. 00399 */ 00400 static void BuildLoginPage (CdcRequest& req, CdcResponse& res, bool stale); 00401 00402 /** 00403 * Find the ID (index in a vector) of the user that sent this request. 00404 * If the ID cannot be found, or if it is wrong - the function returns -1. 00405 * In addition, the function tries to find if a DAA request was made. 00406 * Note: This function returns 3 variables and an object. 00407 * <p> 00408 * @param req Reference to the request object. 00409 * @param DaaRes [output] This is a reference to an empty DAA Response 00410 * object. The function will fill this object if some kind of response 00411 * exists (DAA or built-in). 00412 * @param isRes [output] The function will set this reference to True if it 00413 * finds a response to a challenge. 00414 * @param isVar [output] The function will set this reference to True if it 00415 * finds the user ID in a request variable. 00416 * @return (int) The ID of the user or -1. 00417 */ 00418 static int GetUserID (const CdcRequest& req, CdcDaaResponse& DaaRes, 00419 bool& isRes, bool& isVar); 00420 00421 /** 00422 * This function will try to fill a DAA response object from the HTTP 00423 * request variables. This is the response that came from a built-in login 00424 * page. 00425 * <p> 00426 * @param req Reference to the request object. 00427 * @param DaaRes Reference to the DAA response object to fill. 00428 * @return True if it finds the response nonce field (and then probably the 00429 * entire DAA response is there). 00430 */ 00431 static bool BuildResFromVar (const CdcRequest& req, CdcDaaResponse& DaaRes); 00432 00433 /** 00434 * This function will try to fill DAA response object from the HTTP request 00435 * Authorization header. This is the response that came from a Digest Access 00436 * Authentication system. 00437 * <p> 00438 * @param req Reference to the request object. 00439 * @param DaaRes Reference to the DAA response object to fill. 00440 * @return True if it found the response Authorization header. 00441 */ 00442 static bool BuildResFromHeader (const CdcRequest& req, CdcDaaResponse& DaaRes); 00443 00444 00445 /** 00446 * Find an ID of a user by searching the database for the username. 00447 * <p> 00448 * @param username The username to search for. 00449 * @return (int) The ID of the user or -1. 00450 */ 00451 static int FindUser (const CdcString& username); 00452 00453 /** 00454 * Find a property value in a request header field, by a property name. 00455 * <p> 00456 * @param name The property name. 00457 * @param head The header to check as CdcString. 00458 * @return (CdcString) the property value or empty string if not 00459 * found. 00460 */ 00461 static CdcString GetProp (const CdcString& name, const CdcString& head); 00462 00463 public: 00464 /** 00465 * This function loads the users database to memory, and initialize some 00466 * cryptographic features, and configuration variables. 00467 * <p> 00468 * @param file_name The name of the users database file. 00469 * @throw (CdcException) If cannot open database file, or cannot allocate 00470 * memory. 00471 */ 00472 static void Load (const char* file_name); 00473 00474 /** 00475 * This function saves the users database to a file. 00476 * <p> 00477 * @param file_name The name of the users database file. 00478 * @throw (CdcException) If cannot open database file. 00479 */ 00480 static void Save (const char* file_name); 00481 00482 /** 00483 * This is the main function of this class. it checks if the user is under 00484 * a certain group, and knows the correct password. (see the description in 00485 * the class description). 00486 * <p> 00487 * @param group The group name this page is protected by. 00488 * @param req Reference to the request object. 00489 * @param res Reference to the response object. 00490 * @param ret_code [output] A return code that can be: 00491 * DC_LM_OK if access is granted 00492 * DC_LM_NONE if the group name was empty. 00493 * DC_LM_SEND if need to send to user other (login/deny/DAA 401) page. 00494 * @param new_page If ret_code is DC_LM_SEND, new_page will get the relative 00495 * path to the new page to send (or will be empty in DAA method), otherwise 00496 * it remains untouched. 00497 * @return (CdcUserStatus*) Pointer to the user status object or NULL. 00498 */ 00499 static CdcUserStatus* CheckAccess (const CdcString& group, CdcRequest& req, 00500 CdcResponse& res, int& ret_code, 00501 CdcString& new_page); 00502 00503 /** 00504 * This function should be called by CdcConnectionThread before starting 00505 * to create any response page. It checks if the user asked to be logged out, 00506 * and if so logged him out. 00507 * <p> 00508 * @param req Reference to the request object. 00509 * @param res Reference to the response object. 00510 * @return (bool) True if user logged out. 00511 */ 00512 static bool CheckLogout (CdcRequest& req, CdcResponse& res); 00513 00514 /** 00515 * Function to add a user to the class users database. 00516 * <p> 00517 * @param username The new username. 00518 * @param password The new password. 00519 * @return (bool) True if the new user added successfully. 00520 * @throw (CdcException) Out of memory exception. 00521 */ 00522 static bool AddUser (const CdcString& username, 00523 const CdcString& password); 00524 00525 /** 00526 * Function to change a user password. 00527 * <p> 00528 * @param username The username of the user. 00529 * @param old_password The old password (for identification). 00530 * @param new_password The new password. 00531 * @return (bool) True if the password has been changed successfully. 00532 */ 00533 static bool ChangePassword (const CdcString& username, 00534 const CdcString& old_password, const CdcString new_password); 00535 00536 /** 00537 * Function to remove users from the class database. 00538 * <p> 00539 * @param username The username of the user to remove. 00540 * @param password The password of the user to remove. 00541 * @return (bool) True if the user was removed successfully. 00542 */ 00543 static bool RemoveUser (const CdcString& username, 00544 const CdcString& password); 00545 00546 /** 00547 * Global function for the cleaning thread (runs in infinite loop). 00548 * <p> 00549 * The problem with the cleaning system is that the function CheckResponse 00550 * can be called many times from many different threads, as if using DAA, 00551 * this is the function that check access before sending any protected page. 00552 * In addition the running time of this function is some what long as this 00553 * is the function that do the response digest calculation on the server 00554 * side. <br> 00555 * If the cleaning thread will delete a taken challenge, the system will 00556 * crush. On the other hand, I don't want to lock the Mutex for the entire 00557 * running time of this function, because then it will mutilate other 00558 * CheckResponse functions that needs to run in other threads. There is no 00559 * reason way these functions cannot run in the same time. <br> 00560 * To solve this problem I use "Taken Challenges" global counter. When 00561 * CheckResponse is called it increases this counter by one, and waits on 00562 * the Mutex. After getting the Mutex it releases it immediately and start 00563 * working. When it is done, it decreases the counter by one. Every time a 00564 * challenge needs to be deleted, a semaphore is signaled (just before 00565 * CheckResponse returns). <br> 00566 * The cleaning thread waits on this semaphore. After getting a signal, it 00567 * sleeps for few seconds (from other reasons - the invalid challenge can 00568 * still be used by the browser, if it is a multi-frame page). After the 00569 * delay the thread calls the cleaning function. The cleaning function 00570 * checks first if the counter value is zero, if not it exits immediately. 00571 * If the counter is zero, it iterates through the challenges list and clean 00572 * every challenge that is marked for deletion, and old unused challenges. 00573 * in the beginning of every iteration cycle, it checks the counter again, 00574 * if it is not zero, it exits immediately, so CheckResponse can start 00575 * working. The uncleared challenges will have to wait for the next signal 00576 * on the semaphore. 00577 * <p> 00578 * @param pParam Pointer to bool (isCleanThreadGo) that tell the function to 00579 * exit (close the thread). 00580 * @return (int) The return code from the thread. 00581 */ 00582 friend int DcClean (void* pParam); 00583 }; 00584 00585 #endif /* __CDC_LOGIN_MANAGER */