@@ -83,7 +83,8 @@ static int _handle_digestmd5_rspauth(xmpp_conn_t *conn,
8383static int _handle_scram_challenge (xmpp_conn_t * conn ,
8484 xmpp_stanza_t * stanza ,
8585 void * userdata );
86- static char * _make_scram_init_msg (xmpp_conn_t * conn );
86+ struct scram_user_data ;
87+ static int _make_scram_init_msg (struct scram_user_data * scram );
8788
8889static int _handle_missing_features_sasl (xmpp_conn_t * conn , void * userdata );
8990static int _handle_missing_bind (xmpp_conn_t * conn , void * userdata );
@@ -243,21 +244,24 @@ _handle_features(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
243244 if (text == NULL )
244245 continue ;
245246
246- if (strcasecmp (text , "PLAIN" ) == 0 )
247+ if (strcasecmp (text , "PLAIN" ) == 0 ) {
247248 conn -> sasl_support |= SASL_MASK_PLAIN ;
248- else if (strcasecmp (text , "EXTERNAL" ) == 0 &&
249- (conn -> tls_client_cert || conn -> tls_client_key ))
249+ } else if (strcasecmp (text , "EXTERNAL" ) == 0 &&
250+ (conn -> tls_client_cert || conn -> tls_client_key )) {
250251 conn -> sasl_support |= SASL_MASK_EXTERNAL ;
251- else if (strcasecmp (text , "DIGEST-MD5" ) == 0 )
252+ } else if (strcasecmp (text , "DIGEST-MD5" ) == 0 ) {
252253 conn -> sasl_support |= SASL_MASK_DIGESTMD5 ;
253- else if (strcasecmp (text , "SCRAM-SHA-1" ) == 0 )
254- conn -> sasl_support |= SASL_MASK_SCRAMSHA1 ;
255- else if (strcasecmp (text , "SCRAM-SHA-256" ) == 0 )
256- conn -> sasl_support |= SASL_MASK_SCRAMSHA256 ;
257- else if (strcasecmp (text , "SCRAM-SHA-512" ) == 0 )
258- conn -> sasl_support |= SASL_MASK_SCRAMSHA512 ;
259- else if (strcasecmp (text , "ANONYMOUS" ) == 0 )
254+ } else if (strcasecmp (text , "ANONYMOUS" ) == 0 ) {
260255 conn -> sasl_support |= SASL_MASK_ANONYMOUS ;
256+ } else {
257+ size_t n ;
258+ for (n = 0 ; n < scram_algs_num ; ++ n ) {
259+ if (strcasecmp (text , scram_algs [n ]-> scram_name ) == 0 ) {
260+ conn -> sasl_support |= scram_algs [n ]-> mask ;
261+ break ;
262+ }
263+ }
264+ }
261265
262266 strophe_free (conn -> ctx , text );
263267 }
@@ -439,7 +443,11 @@ static int _handle_digestmd5_rspauth(xmpp_conn_t *conn,
439443}
440444
441445struct scram_user_data {
446+ xmpp_conn_t * conn ;
447+ int sasl_plus ;
442448 char * scram_init ;
449+ char * channel_binding ;
450+ const char * first_bare ;
443451 const struct hash_alg * alg ;
444452};
445453
@@ -471,8 +479,9 @@ static int _handle_scram_challenge(xmpp_conn_t *conn,
471479 if (!challenge )
472480 goto err ;
473481
474- response = sasl_scram (conn -> ctx , scram_ctx -> alg , challenge ,
475- scram_ctx -> scram_init , conn -> jid , conn -> pass );
482+ response =
483+ sasl_scram (conn -> ctx , scram_ctx -> alg , scram_ctx -> channel_binding ,
484+ challenge , scram_ctx -> first_bare , conn -> jid , conn -> pass );
476485 strophe_free (conn -> ctx , challenge );
477486 if (!response )
478487 goto err ;
@@ -506,7 +515,8 @@ static int _handle_scram_challenge(xmpp_conn_t *conn,
506515 */
507516 rc = _handle_sasl_result (conn , stanza ,
508517 (void * )scram_ctx -> alg -> scram_name );
509- strophe_free (conn -> ctx , scram_ctx -> scram_init );
518+ strophe_free_and_null (conn -> ctx , scram_ctx -> channel_binding );
519+ strophe_free_and_null (conn -> ctx , scram_ctx -> scram_init );
510520 strophe_free (conn -> ctx , scram_ctx );
511521 }
512522
@@ -517,33 +527,97 @@ static int _handle_scram_challenge(xmpp_conn_t *conn,
517527err_free_response :
518528 strophe_free (conn -> ctx , response );
519529err :
520- strophe_free (conn -> ctx , scram_ctx -> scram_init );
530+ strophe_free_and_null (conn -> ctx , scram_ctx -> channel_binding );
531+ strophe_free_and_null (conn -> ctx , scram_ctx -> scram_init );
521532 strophe_free (conn -> ctx , scram_ctx );
522533 disconnect_mem_error (conn );
523534 return 0 ;
524535}
525536
526- static char * _make_scram_init_msg (xmpp_conn_t * conn )
537+ static int _make_scram_init_msg (struct scram_user_data * scram )
527538{
539+ xmpp_conn_t * conn = scram -> conn ;
528540 xmpp_ctx_t * ctx = conn -> ctx ;
529- size_t message_len ;
530- char * node ;
531- char * message ;
532- char nonce [32 ];
541+ const void * binding_data ;
542+ char * node , * message , * binding_type ;
543+ size_t message_len , binding_type_len = 0 , binding_data_len ;
544+ int l , is_secured = xmpp_conn_is_secured (conn );
545+ char buf [64 ];
546+
547+ if (scram -> sasl_plus ) {
548+ if (!is_secured ) {
549+ strophe_error (
550+ ctx , "xmpp" ,
551+ "SASL: Server requested a -PLUS variant to authenticate, "
552+ "but the connection is not secured. This is an error on "
553+ "the server side we can't do anything about." );
554+ return -1 ;
555+ }
556+ if (tls_init_channel_binding (conn -> tls , & binding_type ,
557+ & binding_type_len )) {
558+ return -1 ;
559+ }
560+ /* directly account for the '=' char in 'p=<binding-type>' */
561+ binding_type_len += 1 ;
562+ }
533563
534564 node = xmpp_jid_node (ctx , conn -> jid );
535565 if (!node ) {
536- return NULL ;
566+ return -1 ;
537567 }
538- xmpp_rand_nonce (ctx -> rand , nonce , sizeof (nonce ));
539- message_len = strlen (node ) + strlen (nonce ) + 8 + 1 ;
568+ xmpp_rand_nonce (ctx -> rand , buf , sizeof (buf ));
569+ message_len = strlen (node ) + strlen (buf ) + 8 + binding_type_len + 1 ;
540570 message = strophe_alloc (ctx , message_len );
541571 if (message ) {
542- strophe_snprintf (message , message_len , "n,,n=%s,r=%s" , node , nonce );
572+ /* increase length to account for 'y,,', 'n,,' or 'p,,'.
573+ * In the 'p' case the '=' sign has already been accounted for above.
574+ */
575+ binding_type_len += 3 ;
576+ if (scram -> sasl_plus ) {
577+ l = strophe_snprintf (message , message_len , "p=%s,,n=%s,r=%s" ,
578+ binding_type , node , buf );
579+ } else {
580+ l = strophe_snprintf (message , message_len , "%c,,n=%s,r=%s" ,
581+ is_secured ? 'y' : 'n' , node , buf );
582+ }
583+ if (l < 0 || (size_t )l >= message_len ) {
584+ goto err_out ;
585+ } else {
586+ /* Make `first_bare` point to the 'n' of the client-first-message */
587+ scram -> first_bare = message + binding_type_len ;
588+ memcpy (buf , message , binding_type_len );
589+ if (scram -> sasl_plus ) {
590+ binding_data =
591+ tls_get_channel_binding_data (conn -> tls , & binding_data_len );
592+ if (!binding_data ) {
593+ goto err_out ;
594+ }
595+ if (binding_data_len > sizeof (buf ) - binding_type_len ) {
596+ strophe_error (ctx , "xmpp" ,
597+ "Channel binding data is too long (%zu)" ,
598+ binding_data_len );
599+ goto err_out ;
600+ }
601+ memcpy (& buf [binding_type_len ], binding_data , binding_data_len );
602+ binding_type_len += binding_data_len ;
603+ }
604+ if (scram -> channel_binding )
605+ strophe_free (ctx , scram -> channel_binding );
606+ scram -> channel_binding =
607+ xmpp_base64_encode (ctx , (void * )buf , binding_type_len );
608+ memset (buf , 0 , binding_type_len );
609+ }
543610 }
544611 strophe_free (ctx , node );
612+ scram -> scram_init = message ;
545613
546- return message ;
614+ return message == NULL ? -1 : 0 ;
615+ err_out :
616+ strophe_free (ctx , node );
617+ strophe_free (ctx , message );
618+ scram -> first_bare = NULL ;
619+ scram -> scram_init = NULL ;
620+ return -1 ;
547621}
548622
549623static xmpp_stanza_t * _make_starttls (xmpp_conn_t * conn )
@@ -636,7 +710,7 @@ static void _auth(xmpp_conn_t *conn)
636710 return ;
637711 }
638712
639- if (anonjid && conn -> sasl_support & SASL_MASK_ANONYMOUS ) {
713+ if (anonjid && ( conn -> sasl_support & SASL_MASK_ANONYMOUS ) ) {
640714 /* some crap here */
641715 auth = _make_sasl_auth (conn , "ANONYMOUS" );
642716 if (!auth ) {
@@ -702,22 +776,26 @@ static void _auth(xmpp_conn_t *conn)
702776 "Password hasn't been set, and SASL ANONYMOUS unsupported." );
703777 xmpp_disconnect (conn );
704778 } else if (conn -> sasl_support & SASL_MASK_SCRAM ) {
779+ size_t n ;
705780 scram_ctx = strophe_alloc (conn -> ctx , sizeof (* scram_ctx ));
706- if (conn -> sasl_support & SASL_MASK_SCRAMSHA512 )
707- scram_ctx -> alg = & scram_sha512 ;
708- else if (conn -> sasl_support & SASL_MASK_SCRAMSHA256 )
709- scram_ctx -> alg = & scram_sha256 ;
710- else if (conn -> sasl_support & SASL_MASK_SCRAMSHA1 )
711- scram_ctx -> alg = & scram_sha1 ;
781+ memset (scram_ctx , 0 , sizeof (* scram_ctx ));
782+ for (n = 0 ; n < scram_algs_num ; ++ n ) {
783+ if (conn -> sasl_support & scram_algs [n ]-> mask ) {
784+ scram_ctx -> alg = scram_algs [n ];
785+ break ;
786+ }
787+ }
788+
712789 auth = _make_sasl_auth (conn , scram_ctx -> alg -> scram_name );
713790 if (!auth ) {
714791 disconnect_mem_error (conn );
715792 return ;
716793 }
717794
718- /* don't free scram_init on success */
719- scram_ctx -> scram_init = _make_scram_init_msg (conn );
720- if (!scram_ctx -> scram_init ) {
795+ scram_ctx -> conn = conn ;
796+ scram_ctx -> sasl_plus =
797+ scram_ctx -> alg -> mask & SASL_MASK_SCRAM_PLUS ? 1 : 0 ;
798+ if (_make_scram_init_msg (scram_ctx )) {
721799 strophe_free (conn -> ctx , scram_ctx );
722800 xmpp_stanza_release (auth );
723801 disconnect_mem_error (conn );
@@ -753,7 +831,7 @@ static void _auth(xmpp_conn_t *conn)
753831
754832 send_stanza (conn , auth , XMPP_QUEUE_STROPHE );
755833
756- /* SASL SCRAM-SHA-1 was tried, unset flag */
834+ /* SASL algorithm was tried, unset flag */
757835 conn -> sasl_support &= ~scram_ctx -> alg -> mask ;
758836 } else if (conn -> sasl_support & SASL_MASK_DIGESTMD5 ) {
759837 auth = _make_sasl_auth (conn , "DIGEST-MD5" );
0 commit comments