Index: src/network/httpd.c =================================================================== --- src/network/httpd.c (revision 14308) +++ src/network/httpd.c (working copy) @@ -65,6 +65,9 @@ #define HTTPD_CL_BUFSIZE 10000 #endif +#include +#include "md5.h" + static void httpd_ClientClean( httpd_client_t *cl ); /* each host run in his own thread */ @@ -96,6 +99,7 @@ /* TLS data */ tls_server_t *p_tls; + }; struct httpd_url_t @@ -141,6 +145,16 @@ HTTPD_CLIENT_BIDIR, /* check for reading and get data from cb */ }; +struct authenticator_t +{ + char psz_realm[100]; + char psz_nonce[100]; + char psz_user_name[33]; + char psz_password[33]; + int i_password_is_md5; +}; + + struct httpd_client_t { httpd_url_t *url; @@ -169,13 +183,263 @@ /* TLS data */ tls_session_t *p_tls; + + /*RTSP Authentication*/ + struct authenticator_t current_authenticator; + int i_verified_client; }; + + /***************************************************************************** * Various functions *****************************************************************************/ /*char b64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";*/ + + + +static int rtsp_ParseAuthorizationHeader(char *psz_buf, + char *psz_username, + char *psz_realm, + char *psz_nonce, char *psz_uri, + char *psz_response) +{ + // Initialize the result parameters to default values: + *psz_username = *psz_realm = *psz_nonce = *psz_uri = *psz_response = '\0'; + + // First, find "Authorization:" + while (1) { + if (*psz_buf == '\0') return 0; // not found + if (strncasecmp(psz_buf, "Digest ", 7) == 0) break; + ++psz_buf; + } + + // Then, run through each of the fields, looking for ones we handle: + char const* psz_fields = psz_buf + 7; + while (*psz_fields == ' ') ++psz_fields; + char* psz_parameter = malloc(strlen(psz_fields) + 1); + char* psz_value = malloc(strlen(psz_fields) + 1);; + while (1) { + psz_value[0] = '\0'; + if (sscanf(psz_fields, "%[^=]=\"%[^\"]\"", psz_parameter, psz_value) != 2 && + sscanf(psz_fields, "%[^=]=\"\"", psz_parameter) != 1) + break; + if (strcmp(psz_parameter, "username") == 0) + strcpy(psz_username,psz_value); + else if (strcmp(psz_parameter, "realm") == 0) + strcpy(psz_realm,psz_value); + else if (strcmp(psz_parameter, "nonce") == 0) + strcpy(psz_nonce,psz_value); + else if (strcmp(psz_parameter, "uri") == 0) + strcpy(psz_uri,psz_value); + else if (strcmp(psz_parameter, "response") == 0) + strcpy(psz_response,psz_value); + + psz_fields += strlen(psz_parameter) + 2 /*="*/ + strlen(psz_value) + 1 /*"*/; + while (*psz_fields == ',' || *psz_fields == ' ') ++psz_fields; + // skip over any separating ',' and ' ' chars + if (*psz_fields == '\0' || *psz_fields == '\r' || *psz_fields == '\n') + break; + } + free( psz_parameter); free(psz_value); + return 1; +} + + +static char* rtsp_ComputeDigestResponse(char const* psz_cmd, + char const* psz_url,struct authenticator_t *p_current_authenticator,char *psz_calculated_resp) +{ + // The "response" field is computed as: + // md5(md5(::)::md5(:)) + // or, if "i_password_is_md5" is True: + // md5(::md5(:)) + char psz_ha1_buf[33]; + if (p_current_authenticator->i_password_is_md5) + { + strncpy(psz_ha1_buf, p_current_authenticator->psz_password, 32); + psz_ha1_buf[32] = '\0'; // just in case + } + else + { + unsigned const u_ha1_data_len = strlen(p_current_authenticator->psz_user_name) + 1 + + strlen(p_current_authenticator->psz_realm) + 1 + + strlen(p_current_authenticator->psz_password); + unsigned char* psz_ha1_data = malloc(u_ha1_data_len+1); + sprintf((char*)psz_ha1_data, "%s:%s:%s", p_current_authenticator->psz_user_name, + p_current_authenticator->psz_realm,p_current_authenticator->psz_password); + our_MD5Data(psz_ha1_data, u_ha1_data_len, psz_ha1_buf); + free(psz_ha1_data); + } + + unsigned const u_ha2_data_len = strlen(psz_cmd) + 1 + strlen(psz_url); + unsigned char* psz_ha2_data = malloc((u_ha2_data_len+1)*sizeof(unsigned char)); + sprintf((char*)psz_ha2_data, "%s:%s", psz_cmd, psz_url); + char psz_ha2_buf[33]; + our_MD5Data(psz_ha2_data, u_ha2_data_len, psz_ha2_buf); + free(psz_ha2_data); + + unsigned const u_digest_data_len = 32 + 1 + + strlen(p_current_authenticator->psz_nonce) + 1 + 32; + unsigned char* psz_digest_data = malloc((u_digest_data_len+1)*sizeof(unsigned char)); + sprintf((char*)psz_digest_data, "%s:%s:%s", + psz_ha1_buf, p_current_authenticator->psz_nonce, psz_ha2_buf); + our_MD5Data(psz_digest_data, u_digest_data_len, psz_calculated_resp); + free( psz_digest_data); + return psz_calculated_resp; +} + +static int rtsp_SearchAuthenticationInfo(char const* psz_user,char const* psz_file_name, char* psz_passwd) +{ + + FILE *Input; + if( (Input = fopen( psz_file_name, "r" )) == NULL ) + return 0; + + char psz_line[200]; + char *p_index, *psz_user_name, *psz_pass_value; + + *psz_passwd = '\0'; + + while( fgets( psz_line, 200, Input ) ) + { + /* get rid of line feed */ + if( psz_line[strlen(psz_line)-1] == '\n' ) + psz_line[strlen(psz_line)-1] = (char)0; + /* get rid of return cursor */ + if( psz_line[strlen(psz_line)-1] == '\r' ) + psz_line[strlen(psz_line)-1] = (char)0; + + /* look for user name */ + psz_user_name = psz_line; + psz_pass_value = NULL; + p_index = strchr( psz_line, ':' ); + if( !p_index ) continue; /* this ain't an option!!! */ + + *p_index = (char)0; + psz_pass_value = p_index + 1; + if(strcmp(psz_user_name,psz_user)==0) + { + strcpy(psz_passwd,psz_pass_value); + fclose(Input); + return 1; + } + } + fclose(Input); + + return 0; +} + + + + +static int rtsp_AuthenticationOK(char const* psz_cmd_name, + char const* psz_full_request_str,struct authenticator_t *p_current_authenticator,char const* psz_passwd_file) +{ + + char psz_username[33];char psz_password[33]; char psz_realm[100]; char psz_nonce[100]; + char psz_uri[100]; char psz_response[100]; + int i_success = 0; + + if (p_current_authenticator == NULL) + return 1; + + + do + { + // To authenticate, we first need to have a nonce set up + // from a previous attempt: + if (strlen(p_current_authenticator->psz_nonce) == 0) break; + + // Next, the request needs to contain an "Authorization:" header, + // containing a username, (our) realm, (our) nonce, uri, + // and response string: + if (!rtsp_ParseAuthorizationHeader(psz_full_request_str, + psz_username, psz_realm, psz_nonce, psz_uri, psz_response) + || *psz_username == '\0' + || *psz_realm == '\0' || strcmp(psz_realm, p_current_authenticator->psz_realm) != 0 + || *psz_nonce == '\0' || strcmp(psz_nonce, p_current_authenticator->psz_nonce) != 0 + || *psz_uri == '\0' || *psz_response == '\0') + break; + + // Next, the username has to be known to us: + + if(!rtsp_SearchAuthenticationInfo (psz_username, psz_passwd_file,psz_password)) + break; + if(*psz_password == '\0') + break; + + + strcpy(p_current_authenticator->psz_user_name,psz_username); + strcpy(p_current_authenticator->psz_password,psz_password); + + + // Finally, compute a digest response from the information that we have, + // and compare it to the one that we were given: + char psz_our_response[100]; + + rtsp_ComputeDigestResponse(psz_cmd_name, psz_uri,p_current_authenticator,psz_our_response); + i_success = (strcmp(psz_our_response, psz_response) == 0); + } while (0); + + return i_success; +} + + +int rtsp_get_time_of_day(struct timeval* p_time) { +#if defined(_WIN32_WCE) + /* FILETIME of Jan 1 1970 00:00:00. */ + static const unsigned __int64 epoch = 116444736000000000L; + + FILETIME file_time; + SYSTEMTIME system_time; + ULARGE_INTEGER ularge; + + GetSystemTime(&system_time); + SystemTimeToFileTime(&system_time, &file_time); + ularge.LowPart = file_time.dwLowDateTime; + ularge.HighPart = file_time.dwHighDateTime; + + p_time->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L); + p_time->tv_usec = (long) (system_time.wMilliseconds * 1000); +#else + struct timeb tb; + ftime(&tb); + p_time->tv_sec = tb.time; + p_time->tv_usec = 1000*tb.millitm; +#endif + return 0; +} + + +void rtsp_SetRealmAndRandomNonce(char* psz_realm,struct authenticator_t *p_current_authenticator) +{ + char psz_nonce_buf[33]; + static unsigned counter = 0; + struct { + struct timeval timestamp; + unsigned counter; + } seed_data; + + // resetRealmAndNonce(); + p_current_authenticator->psz_realm[0] = '\0'; + p_current_authenticator->psz_nonce[0] = '\0'; + // Construct data to seed the random nonce: + rtsp_get_time_of_day(&seed_data.timestamp); + seed_data.counter = ++counter; + + // Use MD5 to compute a 'random' nonce from this seed data: + our_MD5Data((unsigned char*)(&seed_data), sizeof seed_data, psz_nonce_buf); //defined in md5.h + + // assignRealmAndNonce(realm, nonceBuf); + strcpy(p_current_authenticator->psz_realm,psz_realm); + strcpy(p_current_authenticator->psz_nonce,psz_nonce_buf); +} + + + + + static void b64_decode( char *dest, char *src ) { int i_level; @@ -1416,6 +1680,9 @@ cl->url = NULL; cl->p_tls = p_tls; + cl->current_authenticator.psz_nonce[0] = '\0'; //No nonce initially + cl->i_verified_client=0; //Not yet check password + httpd_ClientInit( cl ); return cl; @@ -1922,6 +2189,7 @@ stats_Create( host, "active_connections", STATS_ACTIVE_CONNECTIONS, VLC_VAR_INTEGER, STATS_COUNTER ); + while( !host->b_die ) { struct timeval timeout; @@ -2018,7 +2286,7 @@ } else if( i_msg == HTTPD_MSG_CHANNEL ) { - /* We are in BIDIR mode, trigger the callback and then + /* We are in BIDIR mode, trigger the callback and then * check for new data */ if( cl->url && cl->url->catch[i_msg].cb ) { @@ -2055,7 +2323,7 @@ } else if( i_msg == HTTPD_MSG_NONE ) { - if( query->i_proto == HTTPD_PROTO_NONE ) + if( query->i_proto == HTTPD_PROTO_NONE ) { cl->url = NULL; cl->i_state = HTTPD_CLIENT_DEAD; @@ -2152,14 +2420,42 @@ if( strcmp( id, auth ) ) { - httpd_MsgAdd( answer, "WWW-Authenticate", "Basic realm=\"%s\"", url->psz_user ); - /* We fail for all url */ - b_auth_failed = VLC_TRUE; - free( id ); - free( auth ); - break; - } + int i_cseq; + char Realm[100]; + + i_cseq = atoi( httpd_MsgGet( query, "Cseq" ) ); + + char *FileName = var_CreateGetString( host, "rtsp-pwd-file" ); + msg_Info( host, "Use %s as password file", FileName); + + + if(!(cl->i_verified_client) ) + cl->i_verified_client = rtsp_AuthenticationOK ("DESCRIBE", + httpd_MsgGet( query, "Authorization" ),&(cl->current_authenticator),FileName); + free(FileName); + + if(!(cl->i_verified_client) ) + { + b_auth_failed = VLC_TRUE; + + msg_Info( host, "Authentication" ); + httpd_MsgAdd( answer, "Cseq", "%d", i_cseq ); + + char *Realm = var_CreateGetString( host, "rtsp-realm" ); + rtsp_SetRealmAndRandomNonce (Realm,&(cl->current_authenticator)); + free(Realm); + cl->current_authenticator.i_password_is_md5 = var_CreateGetBool( host, "rtsp-masked-pwd" ) ; + + httpd_MsgAdd( answer, "WWW-Authenticate", "Digest realm=\"%s\",nonce=\"%s\"", + cl->current_authenticator.psz_realm,cl->current_authenticator.psz_nonce ); + + free( id ); + free( auth ); + break; + } + } + free( id ); free( auth ); } @@ -2196,6 +2492,7 @@ if( b_hosts_failed ) { + msg_Info( host, "b_hosts_failed" ); answer->i_status = 403; answer->psz_status = strdup( "Forbidden" ); @@ -2220,7 +2517,8 @@ answer->i_status = 401; answer->psz_status = strdup( "Authorization Required" ); - p += sprintf( (char *)p, + /* need to be fixed. I comment it out in the favor of the RTSP authentication + p += sprintf( (char *)p, "" "\n" @@ -2233,7 +2531,7 @@ "
\n" "VideoLAN\n" "\n" - "\n", query->psz_url ); + "\n", query->psz_url );*/ } else { Index: src/misc/modules.c =================================================================== --- src/misc/modules.c (revision 14308) +++ src/misc/modules.c (working copy) @@ -487,6 +487,8 @@ * will list them anyway. */ continue; } + + msg_Dbg( p_this, "SONY module: %s capability %s", p_module->psz_longname ,p_module->psz_capability); /* Test if we have the required CPU */ if( (p_module->i_cpu & p_this->p_libvlc->i_cpu) != p_module->i_cpu ) Index: modules/demux/livedotcom.cpp =================================================================== --- modules/demux/livedotcom.cpp (revision 14308) +++ modules/demux/livedotcom.cpp (working copy) @@ -38,8 +38,9 @@ #include "BasicUsageEnvironment.hh" #include "GroupsockHelper.hh" -#include "liveMedia.hh" +#include "liveMedia.hh" + extern "C" { #include "../access/mms/asf.h" /* Who said ugly ? */ } @@ -67,7 +68,14 @@ #define KASENNA_LONGTEXT N_( "Kasenna server speak an old and unstandard " \ "dialect of RTSP. When you set this parameter, VLC will try this dialect "\ "for communication. In this mode you cannot talk to normal RTSP servers." ) +#define USER_TEXT N_("RTSP user name") +#define USER_LONGTEXT N_("Allows you to modify the user name that will " \ + "be used for the connection.") +#define PASS_TEXT N_("RTSP password") +#define PASS_LONGTEXT N_("Allows you to modify the password that will be " \ + "used for the connection.") + vlc_module_begin(); set_description( _("RTP/RTSP/SDP demuxer (using Live555.com)" ) ); set_capability( "demux2", 50 ); @@ -101,6 +109,10 @@ CACHING_TEXT, CACHING_LONGTEXT, VLC_TRUE ); add_bool( "rtsp-kasenna", VLC_FALSE, NULL, KASENNA_TEXT, KASENNA_LONGTEXT, VLC_TRUE ); + add_string( "rtsp-user", NULL, NULL, USER_TEXT, USER_LONGTEXT, + VLC_TRUE ); + add_string( "rtsp-pwd", NULL, NULL, PASS_TEXT, + PASS_LONGTEXT, VLC_TRUE ); vlc_module_end(); /* TODO: @@ -299,11 +311,22 @@ psz_options = p_sys->rtsp->sendOptionsCmd( psz_url ); if( psz_options ) delete [] psz_options; - p_sdp = (uint8_t*)p_sys->rtsp->describeURL( psz_url, + char *psz_user = var_CreateGetString( p_demux, "rtsp-user" ); + char *psz_pwd = var_CreateGetString( p_demux, "rtsp-pwd" ); + + + if ((*psz_user) && (*psz_pwd)) + p_sdp = (uint8_t*)p_sys->rtsp->describeWithPassword( psz_url,psz_user,psz_pwd); + else + p_sdp = (uint8_t*)p_sys->rtsp->describeURL( psz_url, NULL, var_CreateGetBool( p_demux, "rtsp-kasenna" ) ) ; + + free( psz_user ); + free( psz_pwd ); + if( p_sdp == NULL ) { - msg_Err( p_demux, "describeURL failed (%s)", + msg_Err( p_demux, "Open: describeURL failed (%s)", p_sys->env->getResultMsg() ); free( psz_url ); goto error; @@ -1032,7 +1055,7 @@ free( psz_url ); if( p_sdp == NULL ) { - msg_Err( p_demux, "describeURL failed (%s)", + msg_Err( p_demux, "RollOverTcp: describeURL failed (%s)", p_sys->env->getResultMsg() ); return VLC_EGENERIC; } Index: modules/misc/rtsp.c =================================================================== --- modules/misc/rtsp.c (revision 14308) +++ modules/misc/rtsp.c (working copy) @@ -49,6 +49,15 @@ "\nSyntax is address:port/path. Default is to bind to any address "\ "on port 554, with no path." ) +#define AUTH_TEXT N_("RTSP with authentication") +#define AUTH_LONGTEXT N_("Allows you to launch VOD server with authentication ") +#define MASKED_PASS_TEXT N_("Shadowed password") +#define MASKED_PASS_LONGTEXT N_("Allows you to mask the password in the password file") +#define PASS_FILE_TEXT N_("Password file") +#define PASS_FILE_LONGTEXT N_("Specify the patht= of the password file (less than 100 characters), storing all the authenticated users") +#define REALM_TEXT N_("Realm for password") +#define REALM_LONGTEXT N_("Specify the scope of the password file, also be involved to mask them") + vlc_module_begin(); set_shortname( _("RTSP VoD" ) ); set_description( _("RTSP VoD server") ); @@ -57,7 +66,16 @@ set_capability( "vod server", 1 ); set_callbacks( Open, Close ); add_shortcut( "rtsp" ); + add_string ( "rtsp-host", NULL, NULL, HOST_TEXT, HOST_LONGTEXT, VLC_TRUE ); + add_bool( "rtsp-authen", VLC_FALSE, NULL, AUTH_TEXT, + AUTH_LONGTEXT, VLC_TRUE ); + add_bool( "rtsp-masked-pwd", VLC_FALSE, NULL, MASKED_PASS_TEXT, + MASKED_PASS_LONGTEXT, VLC_TRUE ); + add_string( "rtsp-pwd-file", NULL, NULL, PASS_FILE_TEXT, PASS_FILE_LONGTEXT, + VLC_TRUE ); + add_string( "rtsp-realm", NULL, NULL, REALM_TEXT, REALM_LONGTEXT, + VLC_TRUE ); vlc_module_end(); /***************************************************************************** @@ -276,8 +294,20 @@ p_media->b_raw = VLC_FALSE; asprintf( &p_media->psz_rtsp_path, "%s%s", p_sys->psz_path, psz_name ); - p_media->p_rtsp_url = - httpd_UrlNewUnique( p_sys->p_rtsp_host, p_media->psz_rtsp_path, NULL, + + + + if( var_CreateGetBool( p_vod, "rtsp-authen" ) ) + { + //It is just for signaling that we need user and password + //the valid user and password will be retrieved from a password file + p_media->p_rtsp_url = + httpd_UrlNewUnique( p_sys->p_rtsp_host, p_media->psz_rtsp_path, "user", + "Password", NULL ); + } + else + p_media->p_rtsp_url = + httpd_UrlNewUnique( p_sys->p_rtsp_host, p_media->psz_rtsp_path, NULL, NULL, NULL ); if( !p_media->p_rtsp_url )