int oilsFMSetString( jsonObject* object, const char* field, const char* string ) { if(!(object && field && string)) return -1; osrfLogInternal(OSRF_LOG_MARK, "oilsFMSetString(): Collecing position for field %s", field); int pos = fm_ntop(object->classname, field); if( pos > -1 ) { osrfLogInternal(OSRF_LOG_MARK, "oilsFMSetString(): Setting string " "%s at field %s [position %d]", string, field, pos ); jsonObjectSetIndex( object, pos, jsonNewObject(string) ); return 0; } return -1; }
/* Adds the authentication token to the user cache. The timeout for the auth token is based on the type of login as well as (if type=='opac') the org location id. Returns the event that should be returned to the user. Event must be freed */ static oilsEvent* oilsAuthHandleLoginOK( jsonObject* userObj, const char* uname, const char* type, int orgloc, const char* workstation ) { oilsEvent* response; long timeout; char* wsorg = jsonObjectToSimpleString(oilsFMGetObject(userObj, "ws_ou")); if(wsorg) { /* if there is a workstation, use it for the timeout */ osrfLogDebug( OSRF_LOG_MARK, "Auth session trying workstation id %d for auth timeout", atoi(wsorg)); timeout = oilsAuthGetTimeout( userObj, type, atoi(wsorg) ); free(wsorg); } else { osrfLogDebug( OSRF_LOG_MARK, "Auth session trying org from param [%d] for auth timeout", orgloc ); timeout = oilsAuthGetTimeout( userObj, type, orgloc ); } osrfLogDebug(OSRF_LOG_MARK, "Auth session timeout for %s: %ld", uname, timeout ); char* string = va_list_to_string( "%d.%ld.%s", (long) getpid(), time(NULL), uname ); char* authToken = md5sum(string); char* authKey = va_list_to_string( "%s%s", OILS_AUTH_CACHE_PRFX, authToken ); const char* ws = (workstation) ? workstation : ""; osrfLogActivity(OSRF_LOG_MARK, "successful login: username=%s, authtoken=%s, workstation=%s", uname, authToken, ws ); oilsFMSetString( userObj, "passwd", "" ); jsonObject* cacheObj = jsonParseFmt( "{\"authtime\": %ld}", timeout ); jsonObjectSetKey( cacheObj, "userobj", jsonObjectClone(userObj)); if( !strcmp( type, OILS_AUTH_PERSIST )) { // Add entries for endtime and reset_interval, so that we can gracefully // extend the session a bit if the user is active toward the end of the // timeout originally specified. time_t endtime = time( NULL ) + timeout; jsonObjectSetKey( cacheObj, "endtime", jsonNewNumberObject( (double) endtime ) ); // Reset interval is hard-coded for now, but if we ever want to make it // configurable, this is the place to do it: jsonObjectSetKey( cacheObj, "reset_interval", jsonNewNumberObject( (double) DEFAULT_RESET_INTERVAL )); } osrfCachePutObject( authKey, cacheObj, (time_t) timeout ); jsonObjectFree(cacheObj); osrfLogInternal(OSRF_LOG_MARK, "oilsAuthHandleLoginOK(): Placed user object into cache"); jsonObject* payload = jsonParseFmt( "{ \"authtoken\": \"%s\", \"authtime\": %ld }", authToken, timeout ); response = oilsNewEvent2( OSRF_LOG_MARK, OILS_EVENT_SUCCESS, payload ); free(string); free(authToken); free(authKey); jsonObjectFree(payload); return response; }
/** * Writes a single chunk of multipart/x-mixed-replace content */ static void osrfHttpTranslatorWriteChunk(osrfHttpTranslator* trans, transport_message* msg) { osrfLogInternal(OSRF_LOG_MARK, "sending multipart chunk %s", msg->body); ap_rprintf(trans->apreq, "Content-type: %s\n\n%s\n\n", JSON_CONTENT_TYPE, msg->body); //osrfLogInternal(OSRF_LOG_MARK, "Apache sending data: Content-type: %s\n\n%s\n\n", //JSON_CONTENT_TYPE, msg->body); if(trans->complete) { ap_rprintf(trans->apreq, "--%s--\n", trans->delim); //osrfLogInternal(OSRF_LOG_MARK, "Apache sending data: --%s--\n", trans->delim); } else { ap_rprintf(trans->apreq, "--%s\n", trans->delim); //osrfLogInternal(OSRF_LOG_MARK, "Apache sending data: --%s\n", trans->delim); } ap_rflush(trans->apreq); }
/** @brief Verify the password received from the client. @param ctx The method context. @param userObj An object from the database, representing the user. @param password An obfuscated password received from the client. @return 1 if the password is valid; 0 if it isn't; or -1 upon error. (None of the so-called "passwords" used here are in plaintext. All have been passed through at least one layer of hashing to obfuscate them.) Take the password from the user object. Append it to the username seed from memcache, as stored previously by a call to the init method. Take an md5 hash of the result. Then compare this hash to the password received from the client. In order for the two to match, other than by dumb luck, the client had to construct the password it passed in the same way. That means it neded to know not only the original password (either hashed or plaintext), but also the seed. The latter requirement means that the client process needs either to be the same process that called the init method or to receive the seed from the process that did so. */ static int oilsAuthVerifyPassword( const osrfMethodContext* ctx, const jsonObject* userObj, const char* uname, const char* password ) { // Get the username seed, as stored previously in memcache by the init method char* seed = osrfCacheGetString( "%s%s", OILS_AUTH_CACHE_PRFX, uname ); if(!seed) { return osrfAppRequestRespondException( ctx->session, ctx->request, "No authentication seed found. " "open-ils.auth.authenticate.init must be called first"); } // Get the hashed password from the user object char* realPassword = oilsFMGetString( userObj, "passwd" ); osrfLogInternal(OSRF_LOG_MARK, "oilsAuth retrieved real password: [%s]", realPassword); osrfLogDebug(OSRF_LOG_MARK, "oilsAuth retrieved seed from cache: %s", seed ); // Concatenate them and take an MD5 hash of the result char* maskedPw = md5sum( "%s%s", seed, realPassword ); free(realPassword); free(seed); if( !maskedPw ) { // This happens only if md5sum() runs out of memory free( maskedPw ); return -1; // md5sum() ran out of memory } osrfLogDebug(OSRF_LOG_MARK, "oilsAuth generated masked password %s. " "Testing against provided password %s", maskedPw, password ); int ret = 0; if( !strcmp( maskedPw, password ) ) ret = 1; free(maskedPw); return ret; }
/** @brief Verify the password received from the client. @param ctx The method context. @param userObj An object from the database, representing the user. @param password An obfuscated password received from the client. @return 1 if the password is valid; 0 if it isn't; or -1 upon error. (None of the so-called "passwords" used here are in plaintext. All have been passed through at least one layer of hashing to obfuscate them.) Take the password from the user object. Append it to the username seed from memcache, as stored previously by a call to the init method. Take an md5 hash of the result. Then compare this hash to the password received from the client. In order for the two to match, other than by dumb luck, the client had to construct the password it passed in the same way. That means it neded to know not only the original password (either hashed or plaintext), but also the seed. The latter requirement means that the client process needs either to be the same process that called the init method or to receive the seed from the process that did so. */ static int oilsAuthVerifyPassword( const osrfMethodContext* ctx, const jsonObject* userObj, const char* uname, const char* password, const char* nonce ) { // Get the username seed, as stored previously in memcache by the init method char* seed = osrfCacheGetString( "%s%s%s", OILS_AUTH_CACHE_PRFX, uname, nonce ); if(!seed) { return osrfAppRequestRespondException( ctx->session, ctx->request, "No authentication seed found. " "open-ils.auth.authenticate.init must be called first " " (check that memcached is running and can be connected to) " ); } // We won't be needing the seed again, remove it osrfCacheRemove( "%s%s%s", OILS_AUTH_CACHE_PRFX, uname, nonce ); // Get the hashed password from the user object char* realPassword = oilsFMGetString( userObj, "passwd" ); osrfLogInternal(OSRF_LOG_MARK, "oilsAuth retrieved real password: [%s]", realPassword); osrfLogDebug(OSRF_LOG_MARK, "oilsAuth retrieved seed from cache: %s", seed ); // Concatenate them and take an MD5 hash of the result char* maskedPw = md5sum( "%s%s", seed, realPassword ); free(realPassword); free(seed); if( !maskedPw ) { // This happens only if md5sum() runs out of memory free( maskedPw ); return -1; // md5sum() ran out of memory } osrfLogDebug(OSRF_LOG_MARK, "oilsAuth generated masked password %s. " "Testing against provided password %s", maskedPw, password ); int ret = 0; if( !strcmp( maskedPw, password ) ) ret = 1; free(maskedPw); char* countkey = va_list_to_string( "%s%s%s", OILS_AUTH_CACHE_PRFX, uname, OILS_AUTH_COUNT_SFFX ); jsonObject* countobject = osrfCacheGetObject( countkey ); if(countobject) { long failcount = (long) jsonObjectGetNumber( countobject ); if(failcount >= _oilsAuthBlockCount) { ret = 0; osrfLogInfo(OSRF_LOG_MARK, "oilsAuth found too many recent failures for '%s' : %i, forcing failure state.", uname, failcount); } if(ret == 0) { failcount += 1; } jsonObjectSetNumber( countobject, failcount ); osrfCachePutObject( countkey, countobject, _oilsAuthBlockTimeout ); jsonObjectFree(countobject); } free(countkey); return ret; }
/** * Parse opensrf request and relay the request to the opensrf network. */ static size_t on_message_handler_body(void *data, const WebSocketServer *server, const int type, unsigned char *buffer, const size_t buffer_size) { request_rec *r = server->request(server); jsonObject *msg_wrapper = NULL; // free me const jsonObject *tmp_obj = NULL; const jsonObject *osrf_msg = NULL; const char *service = NULL; const char *thread = NULL; const char *log_xid = NULL; char *msg_body = NULL; char *recipient = NULL; int i; if (buffer_size <= 0) return OK; // generate a new log trace for this request. it // may be replaced by a client-provided trace below. osrfLogMkXid(); osrfLogDebug(OSRF_LOG_MARK, "WS received message size=%d", buffer_size); // buffer may not be \0-terminated, which jsonParse requires char buf[buffer_size + 1]; memcpy(buf, buffer, buffer_size); buf[buffer_size] = '\0'; osrfLogInternal(OSRF_LOG_MARK, "WS received inbound message: %s", buf); msg_wrapper = jsonParse(buf); if (msg_wrapper == NULL) { osrfLogWarning(OSRF_LOG_MARK, "WS Invalid JSON: %s", buf); return HTTP_BAD_REQUEST; } osrf_msg = jsonObjectGetKeyConst(msg_wrapper, "osrf_msg"); if (tmp_obj = jsonObjectGetKeyConst(msg_wrapper, "service")) service = jsonObjectGetString(tmp_obj); if (tmp_obj = jsonObjectGetKeyConst(msg_wrapper, "thread")) thread = jsonObjectGetString(tmp_obj); if (tmp_obj = jsonObjectGetKeyConst(msg_wrapper, "log_xid")) log_xid = jsonObjectGetString(tmp_obj); if (log_xid) { // use the caller-provide log trace id if (strlen(log_xid) > MAX_THREAD_SIZE) { osrfLogWarning(OSRF_LOG_MARK, "WS log_xid exceeds max length"); return HTTP_BAD_REQUEST; } osrfLogForceXid(log_xid); } if (thread) { if (strlen(thread) > MAX_THREAD_SIZE) { osrfLogWarning(OSRF_LOG_MARK, "WS thread exceeds max length"); return HTTP_BAD_REQUEST; } // since clients can provide their own threads at session start time, // the presence of a thread does not guarantee a cached recipient recipient = (char*) apr_hash_get( trans->stateful_session_cache, thread, APR_HASH_KEY_STRING); if (recipient) { osrfLogDebug(OSRF_LOG_MARK, "WS found cached recipient %s", recipient); } } if (!recipient) { if (service) { int size = snprintf(recipient_buf, RECIP_BUF_SIZE - 1, "%s@%s/%s", trans->osrf_router, trans->osrf_domain, service); recipient_buf[size] = '\0'; recipient = recipient_buf; } else { osrfLogWarning(OSRF_LOG_MARK, "WS Unable to determine recipient"); return HTTP_BAD_REQUEST; } } osrfLogDebug(OSRF_LOG_MARK, "WS relaying message to opensrf thread=%s, recipient=%s", thread, recipient); msg_body = extract_inbound_messages( r, service, thread, recipient, osrf_msg); osrfLogInternal(OSRF_LOG_MARK, "WS relaying inbound message: %s", msg_body); transport_message *tmsg = message_init( msg_body, NULL, thread, recipient, NULL); message_set_osrf_xid(tmsg, osrfLogGetXid()); client_send_message(osrf_handle, tmsg); osrfLogClearXid(); message_free(tmsg); jsonObjectFree(msg_wrapper); free(msg_body); last_activity_time = time(NULL); return OK; }