1#[cfg(feature = "std")]
19use std::ffi::CString;
20#[cfg(not(feature = "std"))]
21use alloc::ffi::CString;
22use base64::{engine::general_purpose, Engine};
23
24#[cfg(feature = "std")]
25use std::ffi::c_char;
26#[cfg(not(feature = "std"))]
27use core::ffi::c_char;
28
29
30
31#[cfg(feature = "std")]
32use std::str;
33#[cfg(not(feature = "std"))]
34use core::str;
35
36
37#[cfg(not(feature = "std"))]
38use alloc::string::String;
39
40#[cfg(not(feature = "std"))]
41use alloc::boxed::Box;
42
43#[cfg(feature = "use_ring")]
44use crate::{ScramSha1Ring, ScramSha256Ring, ScramSha512Ring};
45
46use crate::
47{
48 capi_scram_err_map,
49 scram_sync::SyncScramClient,
50 ChannelBindType,
51 ScramAuthClient,
52 ScramCbHelper,
53 ScramClientDyn,
54 ScramKey,
55 ScramNonce,
56 ScramResultClient,
57 ScramSha1RustNative,
58 ScramSha256RustNative,
59 ScramSha512RustNative,
60 ScramTypeAlias,
61 SCRAM_TYPES
62};
63
64use super::
65{
66 common,
67 error::{CApiScramResult, CApiScramRuntimeError},
68 nonce::CApiNonce,
69 CApiScramHasherProvider
70};
71
72const CLIENT_MARKER: u64 = 0xc0FFEEAA_12345678;
73
74#[derive(Debug)]
75pub struct CApiAuthClient
76{
77 username: String,
78 authzid: Option<String>,
79 password: String,
80 key: Option<Box<ScramKey>>,
81}
82
83impl ScramAuthClient for CApiAuthClient
84{
85 fn get_username(&self) -> &str
86 {
87 return &self.username;
88 }
89
90 fn get_authzid(&self) -> Option<&str>
91 {
92 return self.authzid.as_ref().map(|v| v.as_str());
93 }
94
95 fn get_password(&self) -> &str
96 {
97 return &self.password;
98 }
99
100 fn get_scram_keys(&self) -> Option<&ScramKey>
101 {
102 return self.key.as_ref().map(|v| v.as_ref());
103 }
104}
105
106
107impl CApiAuthClient
108{
109 fn new(
110 plain_username: *const c_char,
111 plain_authzid: *const c_char,
112 plain_password: *const c_char,
113 scram_key: *mut ScramKey,
114 ) -> CApiScramResult<Self>
115 {
116 let p_username =
117 common::cstr_to_string(plain_username, "plain text username")?;
118
119 let p_authzid =
120 if plain_authzid.is_null() == false
121 {
122 Some(common::cstr_to_string(plain_authzid, "plain text username")?)
123 }
124 else
125 {
126 None
127 };
128
129 let p_password =
130 common::cstr_to_string(plain_password, "plain text password")?;
131
132 let p_key =
133 if scram_key.is_null() == false
134 {
135 Some(unsafe{ Box::from_raw(scram_key) })
136 }
137 else
138 {
139 None
140 };
141
142 let ret =
143 Self
144 {
145 username: p_username,
146 authzid: p_authzid,
147 password: p_password,
148 key: p_key
149 };
150
151 return Ok(ret);
152 }
153}
154
155#[derive(Debug)]
156struct CApiCbClient {}
157
158impl ScramCbHelper for CApiCbClient
159{
160
161}
162
163impl CApiCbClient
164{
165 fn new() -> Self
166 {
167 return Self{};
168 }
169}
170
171#[repr(C)]
172pub struct CApiScramClient
173{
174 dyn_client: Box<dyn ScramClientDyn>,
175 marker: u64,
176}
177
178impl CApiScramClient
179{
180 pub
181 fn new(client: Box<dyn ScramClientDyn>) -> *mut Self
182 {
183 let api_client =
184 CApiScramClient
185 {
186 dyn_client: client,
187 marker: CLIENT_MARKER,
188 };
189
190 return Box::into_raw(Box::new(api_client));
191 }
192}
193
194
195#[unsafe(no_mangle)]
196pub unsafe extern "C"
197fn capi_scram_client_init(
198 hash_provider: CApiScramHasherProvider,
199 scram_type: *const c_char,
200 plain_username: *const c_char,
201 plain_authzid: *const c_char,
202 plain_password: *const c_char,
203 scram_key: *mut ScramKey,
204 client_nonce: *mut CApiNonce,
205 ignore_exts: bool,
206 client: *mut *mut CApiScramClient,
207 error: *mut *mut CApiScramRuntimeError
208) -> i32
209{
210 if client.is_null() == true
211 {
212 return -7;
213 }
214 else if hash_provider.validate() == false
215 {
216 return -1;
217 }
218
219 let scram_type_res =
220 match common::cstr_to_string(scram_type, "scram_type is not valid")
221 {
222 Ok(r) => r,
223 Err(e) =>
224 {
225 unsafe
226 {
227 if let Some(err) = error.as_mut()
228 {
229 *err = e.into_capi();
230 }
231 }
232
233 return 1;
234 }
235 };
236
237 let scramtype =
238 match SCRAM_TYPES.get_scramtype(scram_type_res).map_err(|e|capi_scram_err_map!(e))
239 {
240 Ok(r) => r,
241 Err(e) =>
242 {
243 unsafe
244 {
245 if let Some(err) = error.as_mut()
246 {
247 *err = e.into_capi();
248 }
249 }
250
251 return 1;
252 }
253 };
254
255 let nonce =
256 if client_nonce.is_null() == true
257 {
258 match ScramNonce::none().map_err(|e| capi_scram_err_map!(e))
259 {
260 Ok(r) => r,
261 Err(e) =>
262 {
263 unsafe
264 {
265 if let Some(err) = error.as_mut()
266 {
267 *err = e.into_capi();
268 }
269 }
270
271 return 1;
272 }
273 }
274 }
275 else
276 {
277 unsafe
278 {
279 match client_nonce.as_mut().unwrap().take()
280 {
281 Ok(r) => r,
282 Err(e) =>
283 {
284 if let Some(err) = error.as_mut()
285 {
286 *err = e.into_capi();
287 }
288
289 return 1;
290 }
291 }
292 }
293 };
294
295 let auth =
296 match CApiAuthClient::new(plain_username, plain_authzid, plain_password, scram_key)
297 {
298 Ok(r) => r,
299 Err(e) =>
300 {
301 unsafe
302 {
303 if let Some(err) = error.as_mut()
304 {
305 *err = e.into_capi();
306 }
307 }
308
309 return 1;
310 }
311 };
312
313 let clientcb = CApiCbClient::new();
314
315 let res =
316 match (hash_provider, scramtype.scram_alias)
317 {
318 #[cfg(not(feature = "exclude_sha1"))]
319 (CApiScramHasherProvider::RustNative, ScramTypeAlias::Sha1) =>
320 {
321 SyncScramClient
322 ::<ScramSha1RustNative, CApiAuthClient, CApiCbClient>
323 ::new(auth, nonce, ChannelBindType::None, clientcb, ignore_exts)
324 .map_err(|e|
325 capi_scram_err_map!(e)
326 )
327 .map(|c| c.make_dyn())
328 },
329 (CApiScramHasherProvider::RustNative, ScramTypeAlias::Sha256 | ScramTypeAlias::Sha256Plus) =>
330 {
331 SyncScramClient
332 ::<ScramSha256RustNative, CApiAuthClient, CApiCbClient>
333 ::new(auth, nonce, ChannelBindType::None, clientcb, ignore_exts)
334 .map_err(|e|
335 capi_scram_err_map!(e)
336 )
337 .map(|c| c.make_dyn())
338 },
339 (CApiScramHasherProvider::RustNative, ScramTypeAlias::Sha512 | ScramTypeAlias::Sha512Plus) =>
340 {
341 SyncScramClient
342 ::<ScramSha512RustNative, CApiAuthClient, CApiCbClient>
343 ::new(auth, nonce, ChannelBindType::None, clientcb, ignore_exts)
344 .map_err(|e|
345 capi_scram_err_map!(e)
346 )
347 .map(|c| c.make_dyn())
348 },
349
350 #[cfg(all(feature = "use_ring", not(feature = "exclude_sha1")))]
351 (CApiScramHasherProvider::RustRing, ScramTypeAlias::Sha1) =>
352 {
353 SyncScramClient
354 ::<ScramSha1Ring, CApiAuthClient, CApiCbClient>
355 ::new(auth, nonce, ChannelBindType::None, clientcb, ignore_exts)
356 .map_err(|e|
357 capi_scram_err_map!(e)
358 )
359 .map(|c| c.make_dyn())
360 },
361
362 #[cfg(all(not(feature = "use_ring"), not(feature = "exclude_sha1")))]
363 (CApiScramHasherProvider::RustRing, ScramTypeAlias::Sha1) =>
364 {
365 crate::capi_scram_err_res!(crate::ScramErrorCode::FeatureNotSupported, crate::ScramServerError::None, "Sha1Ring not available")
366 },
367
368 #[cfg(feature = "use_ring")]
369 (CApiScramHasherProvider::RustRing, ScramTypeAlias::Sha256 | ScramTypeAlias::Sha256Plus) =>
370 {
371 SyncScramClient
372 ::<ScramSha256Ring, CApiAuthClient, CApiCbClient>
373 ::new(auth, nonce, ChannelBindType::None, clientcb, ignore_exts)
374 .map_err(|e|
375 capi_scram_err_map!(e)
376 )
377 .map(|c| c.make_dyn())
378 },
379
380 #[cfg(not(feature = "use_ring"))]
381 (CApiScramHasherProvider::RustRing, ScramTypeAlias::Sha256 | ScramTypeAlias::Sha256Plus) =>
382 {
383 crate::capi_scram_err_res!(crate::ScramErrorCode::FeatureNotSupported, crate::ScramServerError::None, "Sha256Ring not available")
384 },
385
386 #[cfg(feature = "use_ring")]
387 (CApiScramHasherProvider::RustRing, ScramTypeAlias::Sha512 | ScramTypeAlias::Sha512Plus) =>
388 {
389 SyncScramClient
390 ::<ScramSha512Ring, CApiAuthClient, CApiCbClient>
391 ::new(auth, nonce, ChannelBindType::None, clientcb, ignore_exts)
392 .map_err(|e|
393 capi_scram_err_map!(e)
394 )
395 .map(|c| c.make_dyn())
396 },
397
398 #[cfg(not(feature = "use_ring"))]
399 (CApiScramHasherProvider::RustRing, ScramTypeAlias::Sha512 | ScramTypeAlias::Sha512Plus) =>
400 {
401 crate::capi_scram_err_res!(crate::ScramErrorCode::FeatureNotSupported, crate::ScramServerError::None, "Sha512Ring not available")
402 }
403 };
404
405 if res.is_ok() == true
406 {
407 unsafe { *client.as_mut().unwrap() = CApiScramClient::new(res.ok().unwrap()) };
408 return 0;
409 }
410 else
411 {
412 unsafe
413 {
414 if let Some(err) = error.as_mut()
415 {
416 *err = res.err().unwrap().into_capi();
417 }
418 }
419
420 return 1;
421 };
422}
423
424
425
426
427#[unsafe(no_mangle)]
428pub unsafe extern "C"
429fn capi_scram_client_free(client: *mut CApiScramClient)
430{
431 if client.is_null() == true
432 {
433 return;
434 }
435
436 let marker = unsafe { client.as_ref().unwrap().marker };
437 if marker != CLIENT_MARKER
438 {
439 panic!("Assertion trap: invalid client marker: {:X} {:X}", marker, CLIENT_MARKER);
440 }
441
442 let boxed_client = unsafe { Box::from_raw(client)};
443
444
445 drop(boxed_client);
446
447 return;
448}
449
450#[derive(PartialEq, Eq)]
451pub struct CApiScramResultClient
452{
453 res: ScramResultClient,
454 plain: Option<CString>,
455 base64: Option<CString>,
456}
457
458impl From<ScramResultClient> for CApiScramResultClient
459{
460 fn from(value: ScramResultClient) -> Self
461 {
462 return Self{ res: value, plain: None, base64: None };
463 }
464}
465
466#[unsafe(no_mangle)]
467pub unsafe extern "C"
468fn capi_scram_client_result_is_final(msg_res: *mut CApiScramResultClient) -> bool
469{
470 if msg_res.is_null() == true
471 {
472 return false;
473 }
474
475 return ScramResultClient::Completed == unsafe { msg_res.as_ref().unwrap()}.res;
476}
477
478#[unsafe(no_mangle)]
479pub unsafe extern "C"
480fn capi_scram_client_result_plain(msg_res: *mut CApiScramResultClient, msg_raw_out: *mut *const c_char) -> i32
481{
482 if msg_res.is_null() == true
483 {
484 return -1;
485 }
486 else if msg_raw_out.is_null() == true
487 {
488 return -1;
489 }
490
491 let this = unsafe { msg_res.as_mut().unwrap()};
492
493 if this.res.is_output() == false
494 {
495 return 1;
496 };
497
498
499
500 let plain_msg =
501 if let Some(plain) = this.plain.as_ref()
502 {
503 plain.as_ptr()
504 }
505 else
506 {
507 let plain = CString::new(this.res.get_output().unwrap().as_bytes()).unwrap();
508 let ret_plain = plain.as_ptr();
509 this.plain.replace(plain);
510
511 ret_plain
512 };
513
514 unsafe { *(msg_raw_out.as_mut().unwrap()) = plain_msg };
515
516 return 0;
517}
518
519#[unsafe(no_mangle)]
520pub unsafe extern "C"
521fn capi_scram_client_result_base64(msg_res: *mut CApiScramResultClient, msg_base64_out: *mut *const c_char) -> i32
522{
523 if msg_res.is_null() == true
524 {
525 return -1;
526 }
527 else if msg_base64_out.is_null() == true
528 {
529 return -2;
530 }
531
532 let this = unsafe { msg_res.as_mut().unwrap() };
533
534 if this.res.is_output() == false
535 {
536 return 1;
537 };
538
539 let base_msg =
540 if let Some(base) = this.base64.as_ref()
541 {
542 base.as_ptr()
543 }
544 else
545 {
546 let base =
547 CString::new(
548 general_purpose::STANDARD.encode(this.res.get_output().unwrap().as_bytes())
549 )
550 .unwrap();
551
552 let ret_base = base.as_ptr();
553 this.base64.replace(base);
554
555 ret_base
556 };
557
558 unsafe { *msg_base64_out = base_msg };
559
560 return 0;
561}
562
563#[unsafe(no_mangle)]
564pub unsafe extern "C"
565fn capi_scram_client_result_free(msg_res: *mut CApiScramResultClient)
566{
567 if msg_res.is_null() == true
568 {
569 return;
570 }
571
572 drop(unsafe { Box::from_raw(msg_res) });
573
574 return;
575}
576
577#[unsafe(no_mangle)]
578pub unsafe extern "C"
579fn capi_scram_client_init_msg(client: *mut CApiScramClient, init_msg: *mut *mut CApiScramResultClient) -> i32
580{
581 if client.is_null() == true
582 {
583 return -1;
584 }
585 else if init_msg.is_null() == true
586 {
587 return -1;
588 }
589
590 let msg =
591 unsafe { client.as_mut().unwrap().dyn_client.init_client_msg()};
592
593 unsafe
594 {
595 *(init_msg.as_mut().unwrap()) = Box::into_raw(Box::new(CApiScramResultClient::from(msg)));
596 }
597
598 return 0;
599}
600
601#[unsafe(no_mangle)]
602pub unsafe extern "C"
603fn capi_scram_client_parse_resp_plain(client: *mut CApiScramClient, resp_plain: *const c_char,
604 res_out: *mut *mut CApiScramResultClient, error_out: *mut *mut CApiScramRuntimeError) -> i32
605{
606 if client.is_null() == true
607 {
608 return -1;
609 }
610 else if res_out.is_null() == true
611 {
612 return -3;
613 }
614
615 let resp_res =
616 match common::cstr_to_string(resp_plain, "resp_plain is not valid")
617 {
618 Ok(r) => r,
619 Err(e) =>
620 {
621 unsafe
622 {
623 if let Some(err) = error_out.as_mut()
624 {
625 *err = e.into_capi();
626 }
627 }
628
629 return 1;
630 }
631 };
632
633
634 let msg =
635 unsafe
636 {
637 client
638 .as_mut()
639 .unwrap()
640 .dyn_client
641 .parse_response(&resp_res)
642 .map_err(|e|
643 capi_scram_err_map!(e)
644 )
645 .map(|res|
646 Box::into_raw(Box::new(CApiScramResultClient::from(res)))
647 )
648 };
649
650 if msg.is_ok() == true
651 {
652 unsafe { *res_out.as_mut().unwrap() = msg.ok().unwrap() };
653
654 return 0;
655 }
656 else
657 {
658 unsafe
659 {
660 if let Some(err) = error_out.as_mut()
661 {
662 *err = msg.err().unwrap().into_capi();
663 }
664 }
665
666 return 1;
667 }
668}
669
670
671#[unsafe(no_mangle)]
672pub unsafe extern "C"
673fn capi_scram_client_parse_resp_base64(client: *mut CApiScramClient, resp_b64: *const u8, resp_len: usize,
674 res_out: *mut *mut CApiScramResultClient, error_out: *mut *mut CApiScramRuntimeError) -> i32
675{
676 if client.is_null() == true
677 {
678 return -1;
679 }
680 else if res_out.is_null() == true
681 {
682 return -4;
683 }
684
685
686 let resp =
687 match common::carr_to_slice(resp_b64, resp_len, "resp is not valid")
688 {
689 Ok(r) => r,
690 Err(e) =>
691 {
692 unsafe
693 {
694 if let Some(err) = error_out.as_mut()
695 {
696 *err = e.into_capi();
697 }
698 }
699
700 return 1;
701 }
702 };
703
704 let msg =
705 unsafe
706 {
707 client
708 .as_mut()
709 .unwrap()
710 .dyn_client
711 .parse_response_base64(resp)
712 .map_err(|e: crate::ScramRuntimeError|
713 capi_scram_err_map!(e)
714 )
715 .map(|res| Box::into_raw(Box::new(CApiScramResultClient::from(res))))
716 };
717
718 if msg.is_ok() == true
719 {
720 unsafe { *res_out.as_mut().unwrap() = msg.ok().unwrap() };
721
722 return 0;
723 }
724 else
725 {
726 unsafe
727 {
728 if let Some(err) = error_out.as_mut()
729 {
730 *err = msg.err().unwrap().into_capi();
731 }
732 }
733
734 return 1;
735 }
736}
737