|
| 1 | +/* SPDX-License-Identifier: MIT OR GPL-3.0-only */ |
| 2 | +/* test_sasl2_syntax.c |
| 3 | +** libstrophe XMPP client library -- test routines for SASL2 syntax |
| 4 | +** |
| 5 | +** Copyright (C) 2024-2026 libstrophe contributors |
| 6 | +** |
| 7 | +** This software is provided AS-IS with no warranty, either express |
| 8 | +** or implied. |
| 9 | +** |
| 10 | +** This program is dual licensed under the MIT or GPLv3 licenses. |
| 11 | +*/ |
| 12 | + |
| 13 | +#include <stdio.h> |
| 14 | +#include <stdlib.h> |
| 15 | +#include <string.h> |
| 16 | +#include <assert.h> |
| 17 | + |
| 18 | +#include "strophe.h" |
| 19 | +#include "common.h" |
| 20 | +#include "sasl.h" |
| 21 | +#include "scram.h" |
| 22 | +#include "test.h" |
| 23 | + |
| 24 | +/* stubs to build test without whole libstrophe */ |
| 25 | + |
| 26 | +static xmpp_stanza_t *last_sent_stanza = NULL; |
| 27 | + |
| 28 | +void send_stanza(xmpp_conn_t *conn, |
| 29 | + xmpp_stanza_t *stanza, |
| 30 | + xmpp_send_queue_owner_t owner) |
| 31 | +{ |
| 32 | + (void)conn; |
| 33 | + (void)owner; |
| 34 | + if (last_sent_stanza) |
| 35 | + xmpp_stanza_release(last_sent_stanza); |
| 36 | + last_sent_stanza = xmpp_stanza_copy(stanza); |
| 37 | + xmpp_stanza_release(stanza); |
| 38 | +} |
| 39 | + |
| 40 | +void handler_add(xmpp_conn_t *conn, |
| 41 | + xmpp_handler handler, |
| 42 | + const char *ns, |
| 43 | + const char *name, |
| 44 | + const char *type, |
| 45 | + void *userdata) |
| 46 | +{ |
| 47 | + (void)conn; |
| 48 | + (void)handler; |
| 49 | + (void)ns; |
| 50 | + (void)name; |
| 51 | + (void)type; |
| 52 | + (void)userdata; |
| 53 | +} |
| 54 | + |
| 55 | +void conn_disconnect(xmpp_conn_t *conn) |
| 56 | +{ |
| 57 | + (void)conn; |
| 58 | +} |
| 59 | + |
| 60 | +int xmpp_conn_is_secured(xmpp_conn_t *conn) |
| 61 | +{ |
| 62 | + (void)conn; |
| 63 | + return 0; |
| 64 | +} |
| 65 | + |
| 66 | +tls_t *tls_new(xmpp_conn_t *conn) |
| 67 | +{ |
| 68 | + (void)conn; |
| 69 | + return NULL; |
| 70 | +} |
| 71 | + |
| 72 | +void tls_free(tls_t *tls) |
| 73 | +{ |
| 74 | + (void)tls; |
| 75 | +} |
| 76 | + |
| 77 | +void tls_clear_password_cache(xmpp_conn_t *conn) |
| 78 | +{ |
| 79 | + (void)conn; |
| 80 | +} |
| 81 | + |
| 82 | +int tls_init_channel_binding(tls_t *tls, |
| 83 | + const char **binding_prefix, |
| 84 | + size_t *binding_prefix_len) |
| 85 | +{ |
| 86 | + (void)tls; |
| 87 | + (void)binding_prefix; |
| 88 | + (void)binding_prefix_len; |
| 89 | + return 0; |
| 90 | +} |
| 91 | + |
| 92 | +const void *tls_get_channel_binding_data(tls_t *tls, size_t *size) |
| 93 | +{ |
| 94 | + (void)tls; |
| 95 | + (void)size; |
| 96 | + return NULL; |
| 97 | +} |
| 98 | + |
| 99 | +char *tls_id_on_xmppaddr(xmpp_conn_t *conn, unsigned int n) |
| 100 | +{ |
| 101 | + (void)conn; |
| 102 | + (void)n; |
| 103 | + return NULL; |
| 104 | +} |
| 105 | + |
| 106 | +unsigned int tls_id_on_xmppaddr_num(xmpp_conn_t *conn) |
| 107 | +{ |
| 108 | + (void)conn; |
| 109 | + return 0; |
| 110 | +} |
| 111 | + |
| 112 | +void handler_reset_timed(xmpp_conn_t *conn, int user_only) |
| 113 | +{ |
| 114 | + (void)conn; |
| 115 | + (void)user_only; |
| 116 | +} |
| 117 | + |
| 118 | +void handler_add_timed(xmpp_conn_t *conn, |
| 119 | + xmpp_timed_handler handler, |
| 120 | + unsigned long period, |
| 121 | + void *userdata) |
| 122 | +{ |
| 123 | + (void)conn; |
| 124 | + (void)handler; |
| 125 | + (void)period; |
| 126 | + (void)userdata; |
| 127 | +} |
| 128 | + |
| 129 | +void handler_add_id(xmpp_conn_t *conn, |
| 130 | + xmpp_handler handler, |
| 131 | + const char *id, |
| 132 | + void *userdata) |
| 133 | +{ |
| 134 | + (void)conn; |
| 135 | + (void)handler; |
| 136 | + (void)id; |
| 137 | + (void)userdata; |
| 138 | +} |
| 139 | + |
| 140 | +void xmpp_timed_handler_delete(xmpp_conn_t *conn, xmpp_timed_handler handler) |
| 141 | +{ |
| 142 | + (void)conn; |
| 143 | + (void)handler; |
| 144 | +} |
| 145 | + |
| 146 | +void xmpp_disconnect(xmpp_conn_t *conn) |
| 147 | +{ |
| 148 | + (void)conn; |
| 149 | +} |
| 150 | + |
| 151 | +void conn_prepare_reset(xmpp_conn_t *conn, xmpp_open_handler handler) |
| 152 | +{ |
| 153 | + (void)conn; |
| 154 | + (void)handler; |
| 155 | + puts("should not reset connection in SASL2"); |
| 156 | + assert(0); |
| 157 | +} |
| 158 | + |
| 159 | +void conn_open_stream(xmpp_conn_t *conn) |
| 160 | +{ |
| 161 | + (void)conn; |
| 162 | + puts("should not reset connection in SASL2"); |
| 163 | + assert(0); |
| 164 | +} |
| 165 | +void send_raw_string(xmpp_conn_t *conn, const char *fmt, ...) |
| 166 | +{ |
| 167 | + (void)conn; |
| 168 | + (void)fmt; |
| 169 | +} |
| 170 | + |
| 171 | +#define strophe_warn(x, ...) (void)x |
| 172 | +#define strophe_debug(x, ...) (void)x |
| 173 | + |
| 174 | +void strophe_error(const xmpp_ctx_t *ctx, |
| 175 | + const char *area, |
| 176 | + const char *fmt, |
| 177 | + ...) |
| 178 | +{ |
| 179 | + (void)ctx; |
| 180 | + (void)area; |
| 181 | + (void)fmt; |
| 182 | +} |
| 183 | + |
| 184 | +void *strophe_alloc(const xmpp_ctx_t *ctx, size_t size) |
| 185 | +{ |
| 186 | + (void)ctx; |
| 187 | + return malloc(size); |
| 188 | +} |
| 189 | + |
| 190 | +void strophe_free(const xmpp_ctx_t *ctx, void *p) |
| 191 | +{ |
| 192 | + (void)ctx; |
| 193 | + free(p); |
| 194 | +} |
| 195 | + |
| 196 | +void *strophe_realloc(const xmpp_ctx_t *ctx, void *p, size_t size) |
| 197 | +{ |
| 198 | + (void)ctx; |
| 199 | + return realloc(p, size); |
| 200 | +} |
| 201 | + |
| 202 | +xmpp_conn_t *xmpp_conn_new(xmpp_ctx_t *ctx) |
| 203 | +{ |
| 204 | + xmpp_conn_t *conn = malloc(sizeof(*conn)); |
| 205 | + conn->ctx = ctx; |
| 206 | + return conn; |
| 207 | +} |
| 208 | + |
| 209 | +int xmpp_conn_release(xmpp_conn_t *conn) |
| 210 | +{ |
| 211 | + free(conn); |
| 212 | + return 0; |
| 213 | +} |
| 214 | + |
| 215 | +/* include auth.c to access static functions */ |
| 216 | +#include "src/auth.c" |
| 217 | + |
| 218 | +/* If SASL2 isn't set, it shouldn't be used */ |
| 219 | +static int test_make_sasl_auth() |
| 220 | +{ |
| 221 | + xmpp_conn_t conn = {.sasl_support = SASL_MASK_PLAIN}; |
| 222 | + xmpp_stanza_t *auth = |
| 223 | + _make_sasl_auth(&conn, "PLAIN", "AGZvb0BiYXIuY29tAHNlY3JldA=="); |
| 224 | + |
| 225 | + COMPARE(xmpp_stanza_get_name(auth), "auth"); |
| 226 | + COMPARE(xmpp_stanza_get_ns(auth), XMPP_NS_SASL); |
| 227 | + COMPARE(xmpp_stanza_get_attribute(auth, "mechanism"), "PLAIN"); |
| 228 | + |
| 229 | + xmpp_stanza_release(auth); |
| 230 | + return 0; |
| 231 | +} |
| 232 | + |
| 233 | +static int test_make_sasl_auth_v2() |
| 234 | +{ |
| 235 | + xmpp_conn_t conn = {.sasl_support = SASL_MASK_SASL2 | SASL_MASK_PLAIN}; |
| 236 | + xmpp_stanza_t *auth = |
| 237 | + _make_sasl_auth(&conn, "PLAIN", "AGZvb0BiYXIuY29tAHNlY3JldA=="); |
| 238 | + xmpp_stanza_t *initial_response = |
| 239 | + xmpp_stanza_get_child_by_name(auth, "initial-response"); |
| 240 | + |
| 241 | + assert(initial_response != NULL); |
| 242 | + COMPARE(xmpp_stanza_get_name(auth), "authenticate"); |
| 243 | + COMPARE(xmpp_stanza_get_ns(auth), XMPP_NS_SASL2); |
| 244 | + COMPARE(xmpp_stanza_get_attribute(auth, "mechanism"), "PLAIN"); |
| 245 | + COMPARE(xmpp_stanza_get_ns(initial_response), XMPP_NS_SASL2); |
| 246 | + |
| 247 | + xmpp_stanza_release(auth); |
| 248 | + return 0; |
| 249 | +} |
| 250 | + |
| 251 | +static int test_scram_challenge_v2(xmpp_ctx_t *ctx) |
| 252 | +{ |
| 253 | + extern const struct hash_alg scram_sha1; |
| 254 | + xmpp_conn_t conn = {.jid = "foo@bar.com", |
| 255 | + .pass = "secret", |
| 256 | + .sasl_support = SASL_MASK_SASL2 | SASL_MASK_SCRAMSHA1}; |
| 257 | + struct scram_user_data scram_ctx = {.alg = &scram_sha1, |
| 258 | + .first_bare = "n=foo,r=random-nonce", |
| 259 | + .channel_binding = |
| 260 | + strophe_strdup(ctx, "biws")}; |
| 261 | + struct scram_user_data *scram_ctx_heap = malloc(sizeof(*scram_ctx_heap)); |
| 262 | + xmpp_stanza_t *stanza = xmpp_stanza_new(ctx); |
| 263 | + xmpp_stanza_t *tstanza = xmpp_stanza_new(ctx); |
| 264 | + |
| 265 | + *scram_ctx_heap = scram_ctx; |
| 266 | + xmpp_stanza_set_name(stanza, "challenge"); |
| 267 | + xmpp_stanza_set_ns(stanza, XMPP_NS_SASL2); |
| 268 | + xmpp_stanza_set_text(tstanza, "cj1meWtvK2QycGJiRmdITkRyOXBRdG16aHZ1U2EzeTg4" |
| 269 | + "dDBkcGNjOVU3LHM9UVNYQ1IrUTZz" |
| 270 | + "ZWs4YmY5MixpPTQwOTY="); |
| 271 | + xmpp_stanza_add_child(stanza, tstanza); |
| 272 | + xmpp_stanza_release(tstanza); |
| 273 | + |
| 274 | + _handle_scram_challenge(&conn, stanza, scram_ctx_heap); |
| 275 | + |
| 276 | + assert(last_sent_stanza != NULL); |
| 277 | + COMPARE(xmpp_stanza_get_name(last_sent_stanza), "response"); |
| 278 | + COMPARE(xmpp_stanza_get_ns(last_sent_stanza), XMPP_NS_SASL2); |
| 279 | + COMPARE(xmpp_stanza_get_text(last_sent_stanza), |
| 280 | + "Yz1iaXdzLHI9ZnlrbytkMnBiYkZnSE5EcjlwUXRtemh2dVNhM3k4OHQwZHBjYzlVNy" |
| 281 | + "xwPTZCY2VjL3kyM1N1RlhkS1VucFpZTnRSRHJIQT0="); |
| 282 | + |
| 283 | + xmpp_stanza_release(last_sent_stanza); |
| 284 | + last_sent_stanza = NULL; |
| 285 | + xmpp_stanza_release(stanza); |
| 286 | + |
| 287 | + return 0; |
| 288 | +} |
| 289 | + |
| 290 | +static int test_sasl_result_v2_success(xmpp_ctx_t *ctx) |
| 291 | +{ |
| 292 | + xmpp_conn_t conn = {.sasl_support = SASL_MASK_SASL2}; |
| 293 | + xmpp_stanza_t *stanza; |
| 294 | + |
| 295 | + stanza = xmpp_stanza_new(ctx); |
| 296 | + xmpp_stanza_set_name(stanza, "success"); |
| 297 | + xmpp_stanza_set_ns(stanza, XMPP_NS_SASL2); |
| 298 | + |
| 299 | + _handle_sasl_result(&conn, stanza, "SCRAM-SHA-1"); |
| 300 | + |
| 301 | + /* If connection resets, the reset stub will assert failure */ |
| 302 | + |
| 303 | + xmpp_stanza_release(stanza); |
| 304 | + |
| 305 | + return 0; |
| 306 | +} |
| 307 | + |
| 308 | +int main() |
| 309 | +{ |
| 310 | + int ret; |
| 311 | + |
| 312 | + printf("testing _make_sasl_auth (SASL 1.0)... "); |
| 313 | + ret = test_make_sasl_auth_v1(); |
| 314 | + if (ret) { |
| 315 | + printf("failed!\n"); |
| 316 | + return ret; |
| 317 | + } |
| 318 | + printf("ok.\n"); |
| 319 | + |
| 320 | + printf("testing _make_sasl_auth (SASL 2.0)... "); |
| 321 | + ret = test_make_sasl_auth_v2(); |
| 322 | + if (ret) { |
| 323 | + printf("failed!\n"); |
| 324 | + return ret; |
| 325 | + } |
| 326 | + printf("ok.\n"); |
| 327 | + |
| 328 | + printf("testing _handle_scram_challenge (SASL 2.0)... "); |
| 329 | + ret = test_scram_challenge_v2(NULL); |
| 330 | + if (ret) { |
| 331 | + printf("failed!\n"); |
| 332 | + return ret; |
| 333 | + } |
| 334 | + printf("ok.\n"); |
| 335 | + |
| 336 | + printf("testing _handle_sasl_result success (SASL 2.0)... "); |
| 337 | + ret = test_sasl_result_v2_success(NULL); |
| 338 | + if (ret) { |
| 339 | + printf("failed!\n"); |
| 340 | + return ret; |
| 341 | + } |
| 342 | + printf("ok.\n"); |
| 343 | + |
| 344 | + return ret; |
| 345 | +} |
0 commit comments