@@ -83,7 +83,8 @@ static int _handle_digestmd5_rspauth(xmpp_conn_t *conn,
83
83
static int _handle_scram_challenge (xmpp_conn_t * conn ,
84
84
xmpp_stanza_t * stanza ,
85
85
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 );
87
88
88
89
static int _handle_missing_features_sasl (xmpp_conn_t * conn , void * userdata );
89
90
static 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)
243
244
if (text == NULL )
244
245
continue ;
245
246
246
- if (strcasecmp (text , "PLAIN" ) == 0 )
247
+ if (strcasecmp (text , "PLAIN" ) == 0 ) {
247
248
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 )) {
250
251
conn -> sasl_support |= SASL_MASK_EXTERNAL ;
251
- else if (strcasecmp (text , "DIGEST-MD5" ) == 0 )
252
+ } else if (strcasecmp (text , "DIGEST-MD5" ) == 0 ) {
252
253
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 ) {
260
255
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
+ }
261
265
262
266
strophe_free (conn -> ctx , text );
263
267
}
@@ -439,7 +443,11 @@ static int _handle_digestmd5_rspauth(xmpp_conn_t *conn,
439
443
}
440
444
441
445
struct scram_user_data {
446
+ xmpp_conn_t * conn ;
447
+ int sasl_plus ;
442
448
char * scram_init ;
449
+ char * channel_binding ;
450
+ const char * first_bare ;
443
451
const struct hash_alg * alg ;
444
452
};
445
453
@@ -471,8 +479,9 @@ static int _handle_scram_challenge(xmpp_conn_t *conn,
471
479
if (!challenge )
472
480
goto err ;
473
481
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 );
476
485
strophe_free (conn -> ctx , challenge );
477
486
if (!response )
478
487
goto err ;
@@ -506,7 +515,8 @@ static int _handle_scram_challenge(xmpp_conn_t *conn,
506
515
*/
507
516
rc = _handle_sasl_result (conn , stanza ,
508
517
(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 );
510
520
strophe_free (conn -> ctx , scram_ctx );
511
521
}
512
522
@@ -517,33 +527,103 @@ static int _handle_scram_challenge(xmpp_conn_t *conn,
517
527
err_free_response :
518
528
strophe_free (conn -> ctx , response );
519
529
err :
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 );
521
532
strophe_free (conn -> ctx , scram_ctx );
522
533
disconnect_mem_error (conn );
523
534
return 0 ;
524
535
}
525
536
526
- static char * _make_scram_init_msg (xmpp_conn_t * conn )
537
+ static int _make_scram_init_msg (struct scram_user_data * scram )
527
538
{
539
+ xmpp_conn_t * conn = scram -> conn ;
528
540
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
+ const char * binding_type ;
543
+ char * node , * message ;
544
+ size_t message_len , binding_type_len = 0 , binding_data_len ;
545
+ int l , is_secured = xmpp_conn_is_secured (conn );
546
+ /* This buffer must be able to hold:
547
+ * "p=<10 bytes binding type>,,<36 bytes binding data>"
548
+ * + alignment */
549
+ char buf [56 ];
550
+
551
+ if (scram -> sasl_plus ) {
552
+ if (!is_secured ) {
553
+ strophe_error (
554
+ ctx , "xmpp" ,
555
+ "SASL: Server requested a -PLUS variant to authenticate, "
556
+ "but the connection is not secured. This is an error on "
557
+ "the server side we can't do anything about." );
558
+ return -1 ;
559
+ }
560
+ if (tls_init_channel_binding (conn -> tls , & binding_type ,
561
+ & binding_type_len )) {
562
+ return -1 ;
563
+ }
564
+ /* directly account for the '=' char in 'p=<binding-type>' */
565
+ binding_type_len += 1 ;
566
+ }
533
567
534
568
node = xmpp_jid_node (ctx , conn -> jid );
535
569
if (!node ) {
536
- return NULL ;
570
+ return -1 ;
537
571
}
538
- xmpp_rand_nonce (ctx -> rand , nonce , sizeof (nonce ));
539
- message_len = strlen (node ) + strlen (nonce ) + 8 + 1 ;
572
+ /* 32 bytes nonce is enough */
573
+ xmpp_rand_nonce (ctx -> rand , buf , 33 );
574
+ message_len = strlen (node ) + strlen (buf ) + 8 + binding_type_len + 1 ;
540
575
message = strophe_alloc (ctx , message_len );
541
- if (message ) {
542
- strophe_snprintf ( message , message_len , "n,,n=%s,r=%s" , node , nonce ) ;
576
+ if (! message ) {
577
+ goto err_node ;
543
578
}
579
+ /* increase length to account for 'y,,', 'n,,' or 'p,,'.
580
+ * In the 'p' case the '=' sign has already been accounted for above.
581
+ */
582
+ binding_type_len += 3 ;
583
+ if (scram -> sasl_plus ) {
584
+ l = strophe_snprintf (message , message_len , "p=%s,,n=%s,r=%s" ,
585
+ binding_type , node , buf );
586
+ } else {
587
+ l = strophe_snprintf (message , message_len , "%c,,n=%s,r=%s" ,
588
+ is_secured ? 'y' : 'n' , node , buf );
589
+ }
590
+ if (l < 0 || (size_t )l >= message_len ) {
591
+ goto err_msg ;
592
+ }
593
+ if (binding_type_len > sizeof (buf )) {
594
+ goto err_msg ;
595
+ }
596
+ /* Make `first_bare` point to the 'n' of 'n=<node>' of the
597
+ * client-first-message */
598
+ scram -> first_bare = message + binding_type_len ;
599
+ memcpy (buf , message , binding_type_len );
600
+ if (scram -> sasl_plus ) {
601
+ binding_data =
602
+ tls_get_channel_binding_data (conn -> tls , & binding_data_len );
603
+ if (!binding_data ) {
604
+ goto err_msg ;
605
+ }
606
+ if (binding_data_len > sizeof (buf ) - binding_type_len ) {
607
+ strophe_error (ctx , "xmpp" , "Channel binding data is too long (%zu)" ,
608
+ binding_data_len );
609
+ goto err_msg ;
610
+ }
611
+ memcpy (& buf [binding_type_len ], binding_data , binding_data_len );
612
+ binding_type_len += binding_data_len ;
613
+ }
614
+ scram -> channel_binding =
615
+ xmpp_base64_encode (ctx , (void * )buf , binding_type_len );
616
+ memset (buf , 0 , binding_type_len );
544
617
strophe_free (ctx , node );
618
+ scram -> scram_init = message ;
619
+
620
+ return 0 ;
545
621
546
- return message ;
622
+ err_msg :
623
+ strophe_free (ctx , message );
624
+ err_node :
625
+ strophe_free (ctx , node );
626
+ return -1 ;
547
627
}
548
628
549
629
static xmpp_stanza_t * _make_starttls (xmpp_conn_t * conn )
@@ -636,7 +716,7 @@ static void _auth(xmpp_conn_t *conn)
636
716
return ;
637
717
}
638
718
639
- if (anonjid && conn -> sasl_support & SASL_MASK_ANONYMOUS ) {
719
+ if (anonjid && ( conn -> sasl_support & SASL_MASK_ANONYMOUS ) ) {
640
720
/* some crap here */
641
721
auth = _make_sasl_auth (conn , "ANONYMOUS" );
642
722
if (!auth ) {
@@ -702,22 +782,26 @@ static void _auth(xmpp_conn_t *conn)
702
782
"Password hasn't been set, and SASL ANONYMOUS unsupported." );
703
783
xmpp_disconnect (conn );
704
784
} else if (conn -> sasl_support & SASL_MASK_SCRAM ) {
785
+ size_t n ;
705
786
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 ;
787
+ memset (scram_ctx , 0 , sizeof (* scram_ctx ));
788
+ for (n = 0 ; n < scram_algs_num ; ++ n ) {
789
+ if (conn -> sasl_support & scram_algs [n ]-> mask ) {
790
+ scram_ctx -> alg = scram_algs [n ];
791
+ break ;
792
+ }
793
+ }
794
+
712
795
auth = _make_sasl_auth (conn , scram_ctx -> alg -> scram_name );
713
796
if (!auth ) {
714
797
disconnect_mem_error (conn );
715
798
return ;
716
799
}
717
800
718
- /* don't free scram_init on success */
719
- scram_ctx -> scram_init = _make_scram_init_msg (conn );
720
- if (!scram_ctx -> scram_init ) {
801
+ scram_ctx -> conn = conn ;
802
+ scram_ctx -> sasl_plus =
803
+ scram_ctx -> alg -> mask & SASL_MASK_SCRAM_PLUS ? 1 : 0 ;
804
+ if (_make_scram_init_msg (scram_ctx )) {
721
805
strophe_free (conn -> ctx , scram_ctx );
722
806
xmpp_stanza_release (auth );
723
807
disconnect_mem_error (conn );
@@ -753,7 +837,7 @@ static void _auth(xmpp_conn_t *conn)
753
837
754
838
send_stanza (conn , auth , XMPP_QUEUE_STROPHE );
755
839
756
- /* SASL SCRAM-SHA-1 was tried, unset flag */
840
+ /* SASL algorithm was tried, unset flag */
757
841
conn -> sasl_support &= ~scram_ctx -> alg -> mask ;
758
842
} else if (conn -> sasl_support & SASL_MASK_DIGESTMD5 ) {
759
843
auth = _make_sasl_auth (conn , "DIGEST-MD5" );
0 commit comments