... | ... |
@@ -47,6 +47,7 @@ EXPORT_SESSION_EVENT_HANDLER_FACTORY(UACAuthFactory, MOD_NAME); |
47 | 47 |
EXPORT_PLUGIN_CLASS_FACTORY(UACAuthFactory, MOD_NAME); |
48 | 48 |
|
49 | 49 |
UACAuthFactory* UACAuthFactory::_instance=0; |
50 |
+string UACAuth::server_nonce_secret = "CKASLD§$>NLKJSLDKFJ"; // replaced on load |
|
50 | 51 |
|
51 | 52 |
UACAuthFactory* UACAuthFactory::instance() |
52 | 53 |
{ |
... | ... |
@@ -57,7 +58,7 @@ UACAuthFactory* UACAuthFactory::instance() |
57 | 58 |
|
58 | 59 |
void UACAuthFactory::invoke(const string& method, const AmArg& args, AmArg& ret) |
59 | 60 |
{ |
60 |
- if(method == "getHandler"){ |
|
61 |
+ if (method == "getHandler") { |
|
61 | 62 |
CredentialHolder* c = dynamic_cast<CredentialHolder*>(args.get(0).asObject()); |
62 | 63 |
DialogControl* cc = dynamic_cast<DialogControl*>(args.get(1).asObject()); |
63 | 64 |
|
... | ... |
@@ -69,14 +70,43 @@ void UACAuthFactory::invoke(const string& method, const AmArg& args, AmArg& ret) |
69 | 70 |
ERROR("wrong types in call to getHandler. (c=%ld, cc= %ld)\n", |
70 | 71 |
(unsigned long)c, (unsigned long)cc); |
71 | 72 |
} |
72 |
- } |
|
73 |
- else |
|
73 |
+ } else if (method == "checkAuth") { |
|
74 |
+ |
|
75 |
+ // params: Request realm user pwd |
|
76 |
+ if (args.size() < 4) { |
|
77 |
+ ERROR("missing arguments to uac_auth checkAuth function, expected Request realm user pwd\n"); |
|
78 |
+ throw AmArg::TypeMismatchException(); |
|
79 |
+ } |
|
80 |
+ |
|
81 |
+ AmSipRequest* req = dynamic_cast<AmSipRequest*>(args.get(0).asObject()); |
|
82 |
+ if (NULL == req) |
|
83 |
+ throw AmArg::TypeMismatchException(); |
|
84 |
+ UACAuth::checkAuthentication(req, args.get(1).asCStr(), |
|
85 |
+ args.get(2).asCStr(), |
|
86 |
+ args.get(3).asCStr(), ret); |
|
87 |
+ } else |
|
74 | 88 |
throw AmDynInvoke::NotImplemented(method); |
75 | 89 |
} |
76 | 90 |
|
77 | 91 |
|
78 | 92 |
int UACAuthFactory::onLoad() |
79 | 93 |
{ |
94 |
+ string secret; |
|
95 |
+ AmConfigReader conf; |
|
96 |
+ string cfg_file_path = AmConfig::ModConfigPath + "uac_auth.conf"; |
|
97 |
+ if(conf.loadFile(cfg_file_path)){ |
|
98 |
+ WARN("Could not open '%s', assuming that default values are fine\n", |
|
99 |
+ cfg_file_path.c_str()); |
|
100 |
+ secret = AmSession::getNewId(); // ?? TODO: is this cryptoproof? |
|
101 |
+ } else { |
|
102 |
+ secret = conf.getParameter("server_secret"); |
|
103 |
+ if (secret.size()<5) { |
|
104 |
+ ERROR("server_secret in '%s' too short!\n", cfg_file_path.c_str()); |
|
105 |
+ return -1; |
|
106 |
+ } |
|
107 |
+ } |
|
108 |
+ |
|
109 |
+ UACAuth::setServerSecret(secret); |
|
80 | 110 |
return 0; |
81 | 111 |
} |
82 | 112 |
|
... | ... |
@@ -263,21 +293,32 @@ void w_MD5Update(MD5_CTX *ctx, const string& s) { |
263 | 293 |
MD5Update(ctx, a, s.length()); |
264 | 294 |
} |
265 | 295 |
|
266 |
- |
|
296 |
+// supr gly |
|
267 | 297 |
string UACAuth::find_attribute(const string& name, const string& header) { |
268 |
- string res; |
|
269 | 298 |
size_t pos1 = header.find(name); |
270 |
- if (pos1!=string::npos) { |
|
271 |
- pos1+=name.length(); |
|
272 |
- pos1 = header.find_first_not_of(" =\"", pos1); |
|
273 |
- if (pos1 != string::npos) { |
|
274 |
- size_t pos2 = header.find_first_of(",\"", pos1); |
|
275 |
- if (pos2 != string::npos) { |
|
276 |
- res = header.substr(pos1, pos2-pos1); |
|
277 |
- } |
|
299 |
+ |
|
300 |
+ while (true) { |
|
301 |
+ if (pos1 == string::npos) |
|
302 |
+ return ""; |
|
303 |
+ |
|
304 |
+ if (!pos1 || header[pos1-1] == ',' || header[pos1-1] == ' ') |
|
305 |
+ break; |
|
306 |
+ |
|
307 |
+ pos1 = header.find(name, pos1+1); |
|
308 |
+ } |
|
309 |
+ |
|
310 |
+ pos1+=name.length(); |
|
311 |
+ pos1 = header.find_first_not_of(" =\"", pos1); |
|
312 |
+ if (pos1 != string::npos) { |
|
313 |
+ size_t pos2 = header.find_first_of(",\"", pos1); |
|
314 |
+ if (pos2 != string::npos) { |
|
315 |
+ return header.substr(pos1, pos2-pos1); |
|
316 |
+ } else { |
|
317 |
+ return header.substr(pos1); // end of hdr |
|
278 | 318 |
} |
279 | 319 |
} |
280 |
- return res; |
|
320 |
+ |
|
321 |
+ return ""; |
|
281 | 322 |
} |
282 | 323 |
|
283 | 324 |
bool UACAuth::parse_header(const string& auth_hdr, UACAuthDigestChallenge& challenge) { |
... | ... |
@@ -376,9 +417,9 @@ bool UACAuth::do_auth(const UACAuthDigestChallenge& challenge, |
376 | 417 |
} |
377 | 418 |
|
378 | 419 |
/* do authentication */ |
379 |
- uac_calc_HA1( challenge, cnonce, ha1); |
|
420 |
+ uac_calc_HA1( challenge, credential, cnonce, ha1); |
|
380 | 421 |
uac_calc_HA2( method, uri, challenge, qop_auth_int ? hentity : NULL, ha2); |
381 |
- uac_calc_response( ha1, ha2, challenge, cnonce, qop_value, response); |
|
422 |
+ uac_calc_response( ha1, ha2, challenge, cnonce, qop_value, nonce_count, response); |
|
382 | 423 |
DBG("calculated response = %s\n", response); |
383 | 424 |
|
384 | 425 |
// compile auth response |
... | ... |
@@ -409,19 +450,23 @@ bool UACAuth::do_auth(const UACAuthDigestChallenge& challenge, |
409 | 450 |
* calculate H(A1) |
410 | 451 |
*/ |
411 | 452 |
void UACAuth::uac_calc_HA1(const UACAuthDigestChallenge& challenge, |
453 |
+ const UACAuthCred* _credential, |
|
412 | 454 |
string cnonce, |
413 | 455 |
HASHHEX sess_key) |
414 | 456 |
{ |
457 |
+ if (NULL == _credential) |
|
458 |
+ return; |
|
459 |
+ |
|
415 | 460 |
MD5_CTX Md5Ctx; |
416 | 461 |
HASH HA1; |
417 | 462 |
|
418 | 463 |
MD5Init(&Md5Ctx); |
419 |
- w_MD5Update(&Md5Ctx, credential->user); |
|
464 |
+ w_MD5Update(&Md5Ctx, _credential->user); |
|
420 | 465 |
w_MD5Update(&Md5Ctx, ":"); |
421 | 466 |
// use realm from challenge |
422 | 467 |
w_MD5Update(&Md5Ctx, challenge.realm); |
423 | 468 |
w_MD5Update(&Md5Ctx, ":"); |
424 |
- w_MD5Update(&Md5Ctx, credential->pwd); |
|
469 |
+ w_MD5Update(&Md5Ctx, _credential->pwd); |
|
425 | 470 |
MD5Final(HA1, &Md5Ctx); |
426 | 471 |
|
427 | 472 |
// MD5sess ...not supported |
... | ... |
@@ -486,7 +531,7 @@ void UACAuth::uac_calc_hentity( const string& body, HASHHEX hentity ) |
486 | 531 |
*/ |
487 | 532 |
void UACAuth::uac_calc_response(HASHHEX ha1, HASHHEX ha2, |
488 | 533 |
const UACAuthDigestChallenge& challenge, const string& cnonce, |
489 |
- const string& qop_value, HASHHEX response) |
|
534 |
+ const string& qop_value, unsigned int nonce_count, HASHHEX response) |
|
490 | 535 |
{ |
491 | 536 |
unsigned char hc[1]; hc[0]=':'; |
492 | 537 |
MD5_CTX Md5Ctx; |
... | ... |
@@ -498,6 +543,7 @@ void UACAuth::uac_calc_response(HASHHEX ha1, HASHHEX ha2, |
498 | 543 |
w_MD5Update(&Md5Ctx, challenge.nonce); |
499 | 544 |
MD5Update(&Md5Ctx, hc, 1); |
500 | 545 |
|
546 |
+ |
|
501 | 547 |
if (!qop_value.empty()) { |
502 | 548 |
|
503 | 549 |
w_MD5Update(&Md5Ctx, int2hex(nonce_count,true)); |
... | ... |
@@ -512,3 +558,176 @@ void UACAuth::uac_calc_response(HASHHEX ha1, HASHHEX ha2, |
512 | 558 |
MD5Final(RespHash, &Md5Ctx); |
513 | 559 |
cvt_hex(RespHash, response); |
514 | 560 |
} |
561 |
+ |
|
562 |
+/** calculate nonce: time-stamp H(time-stamp private-key) */ |
|
563 |
+string UACAuth::calcNonce() { |
|
564 |
+ string result; |
|
565 |
+ HASHHEX hash; |
|
566 |
+ MD5_CTX Md5Ctx; |
|
567 |
+ HASH RespHash; |
|
568 |
+ |
|
569 |
+ time_t now = time(NULL); |
|
570 |
+ result = int2hex(now); |
|
571 |
+ |
|
572 |
+ MD5Init(&Md5Ctx); |
|
573 |
+ w_MD5Update(&Md5Ctx, result); |
|
574 |
+ w_MD5Update(&Md5Ctx, server_nonce_secret); |
|
575 |
+ MD5Final(RespHash, &Md5Ctx); |
|
576 |
+ cvt_hex(RespHash, hash); |
|
577 |
+ |
|
578 |
+ return result+string((const char*)hash); |
|
579 |
+} |
|
580 |
+ |
|
581 |
+/** check nonce integrity. @return true if correct */ |
|
582 |
+bool UACAuth::checkNonce(const string& nonce) { |
|
583 |
+ HASHHEX hash; |
|
584 |
+ MD5_CTX Md5Ctx; |
|
585 |
+ HASH RespHash; |
|
586 |
+ |
|
587 |
+#define INT_HEX_LEN int(2*sizeof(int)) |
|
588 |
+ |
|
589 |
+ if (nonce.size() != INT_HEX_LEN+HASHHEXLEN) { |
|
590 |
+ DBG("wrong nonce length (expected %u, got %zd)\n", INT_HEX_LEN+HASHHEXLEN, nonce.size()); |
|
591 |
+ return false; |
|
592 |
+ } |
|
593 |
+ |
|
594 |
+ MD5Init(&Md5Ctx); |
|
595 |
+ w_MD5Update(&Md5Ctx, nonce.substr(0,INT_HEX_LEN)); |
|
596 |
+ w_MD5Update(&Md5Ctx, server_nonce_secret); |
|
597 |
+ MD5Final(RespHash, &Md5Ctx); |
|
598 |
+ cvt_hex(RespHash, hash); |
|
599 |
+ |
|
600 |
+ return !strncmp((const char*)hash, &nonce[INT_HEX_LEN], HASHHEXLEN); |
|
601 |
+} |
|
602 |
+ |
|
603 |
+void UACAuth::setServerSecret(const string& secret) { |
|
604 |
+ server_nonce_secret = secret; |
|
605 |
+ DBG("Server Nonce secret set\n"); |
|
606 |
+} |
|
607 |
+ |
|
608 |
+void UACAuth::checkAuthentication(const AmSipRequest* req, const string& realm, const string& user, |
|
609 |
+ const string& pwd, AmArg& ret) { |
|
610 |
+ if (req->method == SIP_METH_ACK || req->method == SIP_METH_CANCEL) { |
|
611 |
+ DBG("letting pass %s request without authentication\n", req->method.c_str()); |
|
612 |
+ ret.push(200); |
|
613 |
+ ret.push("OK"); |
|
614 |
+ ret.push(""); |
|
615 |
+ return; |
|
616 |
+ } |
|
617 |
+ |
|
618 |
+ string auth_hdr = getHeader(req->hdrs, "Authorization"); |
|
619 |
+ bool authenticated = false; |
|
620 |
+ |
|
621 |
+ if (auth_hdr.size()) { |
|
622 |
+ UACAuthDigestChallenge r_challenge; |
|
623 |
+ |
|
624 |
+ r_challenge.realm = find_attribute("realm", auth_hdr); |
|
625 |
+ r_challenge.nonce = find_attribute("nonce", auth_hdr); |
|
626 |
+ r_challenge.qop = find_attribute("qop", auth_hdr); |
|
627 |
+ string r_response = find_attribute("response", auth_hdr); |
|
628 |
+ string r_username = find_attribute("username", auth_hdr); |
|
629 |
+ string r_uri = find_attribute("uri", auth_hdr); |
|
630 |
+ string r_cnonce = find_attribute("cnonce", auth_hdr); |
|
631 |
+ |
|
632 |
+ DBG("got realm '%s' nonce '%s', qop '%s', response '%s', username '%s' uri '%s' cnonce '%s'\n", |
|
633 |
+ r_challenge.realm.c_str(), r_challenge.nonce.c_str(), r_challenge.qop.c_str(), |
|
634 |
+ r_response.c_str(), r_username.c_str(), r_uri.c_str(), r_cnonce.c_str() ); |
|
635 |
+ |
|
636 |
+ if (r_response.size() != HASHHEXLEN) { |
|
637 |
+ DBG("Auth: response length mismatch (wanted %u hex chars): '%s'\n", HASHHEXLEN, r_response.c_str()); |
|
638 |
+ goto auth_end; |
|
639 |
+ } |
|
640 |
+ |
|
641 |
+ if (realm != r_challenge.realm) { |
|
642 |
+ DBG("Auth: realm mismatch: required '%s' vs '%s'\n", realm.c_str(), r_challenge.realm.c_str()); |
|
643 |
+ goto auth_end; |
|
644 |
+ } |
|
645 |
+ |
|
646 |
+ if (user != r_username) { |
|
647 |
+ DBG("Auth: user mismatch: '%s' vs '%s'\n", user.c_str(), r_username.c_str()); |
|
648 |
+ goto auth_end; |
|
649 |
+ } |
|
650 |
+ |
|
651 |
+ if (!checkNonce(r_challenge.nonce)) { |
|
652 |
+ DBG("Auth: incorrect nonce '%s'\n", r_challenge.nonce.c_str()); |
|
653 |
+ goto auth_end; |
|
654 |
+ } |
|
655 |
+ |
|
656 |
+ // we don't check the URI |
|
657 |
+ // if (r_uri != req->r_uri) { |
|
658 |
+ // DBG("Auth: incorrect URI in request: '%s'\n", r_challenge.nonce.c_str()); |
|
659 |
+ // goto auth_end; |
|
660 |
+ // } |
|
661 |
+ |
|
662 |
+ UACAuthCred credential; |
|
663 |
+ credential.user = user; |
|
664 |
+ credential.pwd = pwd; |
|
665 |
+ |
|
666 |
+ unsigned int client_nonce_count = 1; |
|
667 |
+ |
|
668 |
+ HASHHEX ha1; |
|
669 |
+ HASHHEX ha2; |
|
670 |
+ HASHHEX hentity; |
|
671 |
+ HASHHEX response; |
|
672 |
+ bool qop_auth=false; |
|
673 |
+ bool qop_auth_int=false; |
|
674 |
+ string qop_value; |
|
675 |
+ |
|
676 |
+ if(!r_challenge.qop.empty()){ |
|
677 |
+ |
|
678 |
+ if (r_challenge.qop == "auth") |
|
679 |
+ qop_auth = true; |
|
680 |
+ else if (r_challenge.qop == "auth-int") |
|
681 |
+ qop_auth_int = true; |
|
682 |
+ |
|
683 |
+ if(qop_auth || qop_auth_int) { |
|
684 |
+ |
|
685 |
+ // get nonce count from request |
|
686 |
+ string nonce_count_str = find_attribute("nc", auth_hdr); |
|
687 |
+ if (str2i(nonce_count_str, client_nonce_count)) { |
|
688 |
+ DBG("Error parsing nonce_count '%s'\n", nonce_count_str.c_str()); |
|
689 |
+ goto auth_end; |
|
690 |
+ } |
|
691 |
+ |
|
692 |
+ DBG("got client_nonce_count %u\n", client_nonce_count); |
|
693 |
+ |
|
694 |
+ // auth-int? calculate hentity |
|
695 |
+ if(qop_auth_int){ |
|
696 |
+ string body_str; |
|
697 |
+ if(!req->body.empty()) req->body.print(body_str); |
|
698 |
+ uac_calc_hentity(body_str, hentity); |
|
699 |
+ qop_value = "auth-int"; |
|
700 |
+ } else { |
|
701 |
+ qop_value = "auth"; |
|
702 |
+ } |
|
703 |
+ |
|
704 |
+ } |
|
705 |
+ } |
|
706 |
+ |
|
707 |
+ uac_calc_HA1(r_challenge, &credential, r_cnonce, ha1); |
|
708 |
+ uac_calc_HA2(req->method, r_uri, r_challenge, qop_auth_int ? hentity : NULL, ha2); |
|
709 |
+ uac_calc_response( ha1, ha2, r_challenge, r_cnonce, qop_value, client_nonce_count, response); |
|
710 |
+ DBG("calculated our response vs request: '%s' vs '%s'", response, r_response.c_str()); |
|
711 |
+ |
|
712 |
+ if (!strncmp((const char*)response, r_response.c_str(), HASHHEXLEN)) { |
|
713 |
+ DBG("Auth: authentication successfull\n"); |
|
714 |
+ authenticated = true; |
|
715 |
+ } else { |
|
716 |
+ DBG("Auth: authentication NOT successfull\n"); |
|
717 |
+ } |
|
718 |
+ } |
|
719 |
+ |
|
720 |
+ auth_end: |
|
721 |
+ if (authenticated) { |
|
722 |
+ ret.push(200); |
|
723 |
+ ret.push("OK"); |
|
724 |
+ ret.push(""); |
|
725 |
+ } else { |
|
726 |
+ ret.push(401); |
|
727 |
+ ret.push("Unauthorized"); |
|
728 |
+ ret.push(SIP_HDR_COLSP(SIP_HDR_WWW_AUTHENTICATE) "Digest " |
|
729 |
+ "realm=\""+realm+"\", " |
|
730 |
+ "qop=\"auth,auth-int\", " |
|
731 |
+ "nonce=\""+calcNonce()+"\"\r\n"); |
|
732 |
+ } |
|
733 |
+} |
... | ... |
@@ -98,6 +98,8 @@ struct SIPRequestInfo { |
98 | 98 |
/** \brief SessionEventHandler for implementing uac authentication */ |
99 | 99 |
class UACAuth : public AmSessionEventHandler |
100 | 100 |
{ |
101 |
+ static string server_nonce_secret; |
|
102 |
+ |
|
101 | 103 |
std::map<unsigned int, SIPRequestInfo> sent_requests; |
102 | 104 |
|
103 | 105 |
UACAuthCred* credential; |
... | ... |
@@ -111,24 +113,26 @@ class UACAuth : public AmSessionEventHandler |
111 | 113 |
|
112 | 114 |
bool nonce_reuse; // reused nonce? |
113 | 115 |
|
114 |
- std::string find_attribute(const std::string& name, const std::string& header); |
|
115 |
- bool parse_header(const std::string& auth_hdr, UACAuthDigestChallenge& challenge); |
|
116 |
+ static std::string find_attribute(const std::string& name, const std::string& header); |
|
117 |
+ static bool parse_header(const std::string& auth_hdr, UACAuthDigestChallenge& challenge); |
|
116 | 118 |
|
117 |
- void uac_calc_HA1(const UACAuthDigestChallenge& challenge, |
|
118 |
- std::string cnonce, |
|
119 |
- HASHHEX sess_key); |
|
119 |
+ static void uac_calc_HA1(const UACAuthDigestChallenge& challenge, |
|
120 |
+ const UACAuthCred* _credential, |
|
121 |
+ std::string cnonce, |
|
122 |
+ HASHHEX sess_key); |
|
120 | 123 |
|
121 |
- void uac_calc_HA2( const std::string& method, const std::string& uri, |
|
122 |
- const UACAuthDigestChallenge& challenge, |
|
123 |
- HASHHEX hentity, |
|
124 |
- HASHHEX HA2Hex ); |
|
124 |
+ static void uac_calc_HA2( const std::string& method, const std::string& uri, |
|
125 |
+ const UACAuthDigestChallenge& challenge, |
|
126 |
+ HASHHEX hentity, |
|
127 |
+ HASHHEX HA2Hex ); |
|
125 | 128 |
|
126 |
- void uac_calc_hentity( const std::string& body, HASHHEX hentity ); |
|
129 |
+ static void uac_calc_hentity( const std::string& body, HASHHEX hentity ); |
|
127 | 130 |
|
128 |
- void uac_calc_response( HASHHEX ha1, HASHHEX ha2, |
|
129 |
- const UACAuthDigestChallenge& challenge, |
|
130 |
- const std::string& cnonce, const string& qop_value, |
|
131 |
- HASHHEX response); |
|
131 |
+ static void uac_calc_response( HASHHEX ha1, HASHHEX ha2, |
|
132 |
+ const UACAuthDigestChallenge& challenge, |
|
133 |
+ const std::string& cnonce, const string& qop_value, |
|
134 |
+ unsigned int nonce_count, |
|
135 |
+ HASHHEX response); |
|
132 | 136 |
|
133 | 137 |
/** |
134 | 138 |
* do auth on cmd with nonce in auth_hdr if possible |
... | ... |
@@ -160,6 +164,13 @@ class UACAuth : public AmSessionEventHandler |
160 | 164 |
AmBasicSipDialog::Status old_status); |
161 | 165 |
virtual bool onSendRequest(AmSipRequest& req, int& flags); |
162 | 166 |
virtual bool onSendReply(const AmSipRequest& req, AmSipReply& reply, int& flags); |
167 |
+ |
|
168 |
+ static string calcNonce(); |
|
169 |
+ static bool checkNonce(const string& nonce); |
|
170 |
+ static void checkAuthentication(const AmSipRequest* req, const string& realm, |
|
171 |
+ const string& user, const string& pwd, AmArg& ret); |
|
172 |
+ |
|
173 |
+ static void setServerSecret(const string& secret); |
|
163 | 174 |
}; |
164 | 175 |
|
165 | 176 |
|
... | ... |
@@ -9,6 +9,9 @@ CORE_HDRS=$(CORE_SRCS:.cpp=.h) |
9 | 9 |
CORE_OBJS=$(CORE_SRCS:.cpp=.o) |
10 | 10 |
CORE_DEPS=$(subst ../,,$(CORE_SRCS:.cpp=.d)) |
11 | 11 |
|
12 |
+AUTH_DIR=../plug-in/uac_auth |
|
13 |
+AUTH_OBJS=$(AUTH_DIR)/UACAuth.o |
|
14 |
+ |
|
12 | 15 |
SRCS=$(wildcard *.cpp) |
13 | 16 |
HDRS=$(SRCS:.cpp=.h) |
14 | 17 |
OBJS=$(SRCS:.cpp=.o) |
... | ... |
@@ -48,6 +51,9 @@ deps: $(DEPS) |
48 | 51 |
.PHONY: core_deps |
49 | 52 |
core_deps: $(CORE_DEPS) |
50 | 53 |
|
54 |
+AUTH_OBJS: $(AUTH_DIR)/UACAuth.cpp $(AUTH_DIR)/UACAuth.h |
|
55 |
+ cd $(AUTH_DIR) ; $(MAKE) AUTH_OBJS |
|
56 |
+ |
|
51 | 57 |
COREPATH=.. |
52 | 58 |
include ../../Makefile.defs |
53 | 59 |
|
... | ... |
@@ -64,7 +70,7 @@ include ../../Makefile.defs |
64 | 70 |
$(NAME): $(OBJS) $(CORE_OBJS) $(SIP_STACK) $(LIBRESAMPLE) ../../Makefile.defs |
65 | 71 |
-@echo "" |
66 | 72 |
-@echo "making $(NAME)" |
67 |
- $(LD) -o $(NAME) $(OBJS) $(CORE_OBJS) $(SIP_STACK) $(LIBRESAMPLE) $(LDFLAGS) $(EXTRA_LDFLAGS) |
|
73 |
+ $(LD) -o $(NAME) $(OBJS) $(CORE_OBJS) $(SIP_STACK) $(LIBRESAMPLE) $(LDFLAGS) $(EXTRA_LDFLAGS) $(AUTH_OBJS) |
|
68 | 74 |
|
69 | 75 |
|
70 | 76 |
ifeq '$(NAME)' '$(MAKECMDGOALS)' |
27 | 29 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,48 @@ |
1 |
+#include "fct.h" |
|
2 |
+ |
|
3 |
+#include "log.h" |
|
4 |
+ |
|
5 |
+#include "AmSipHeaders.h" |
|
6 |
+#include "AmSipMsg.h" |
|
7 |
+#include "AmUtils.h" |
|
8 |
+#include "plug-in/uac_auth/UACAuth.h" |
|
9 |
+ |
|
10 |
+FCTMF_SUITE_BGN(test_auth) { |
|
11 |
+ |
|
12 |
+ FCT_TEST_BGN(nonce_gen) { |
|
13 |
+ |
|
14 |
+ string secret = "1234secret"; |
|
15 |
+ string nonce = UACAuth::calcNonce(secret); |
|
16 |
+ // DBG("nonce '%s'\n", nonce.c_str()); |
|
17 |
+ fct_chk( UACAuth::checkNonce(nonce, secret)); |
|
18 |
+ } FCT_TEST_END(); |
|
19 |
+ |
|
20 |
+ FCT_TEST_BGN(nonce_wrong_secret) { |
|
21 |
+ string secret = "1234secret"; |
|
22 |
+ string nonce = UACAuth::calcNonce(secret); |
|
23 |
+ fct_chk( !UACAuth::checkNonce(nonce, secret+"asd")); |
|
24 |
+ } FCT_TEST_END(); |
|
25 |
+ |
|
26 |
+ FCT_TEST_BGN(nonce_wrong_nonce) { |
|
27 |
+ string secret = "1234secret"; |
|
28 |
+ string nonce = UACAuth::calcNonce(secret); |
|
29 |
+ nonce[0]=0; |
|
30 |
+ nonce[1]=0; |
|
31 |
+ fct_chk( !UACAuth::checkNonce(nonce, secret)); |
|
32 |
+ } FCT_TEST_END(); |
|
33 |
+ |
|
34 |
+ FCT_TEST_BGN(nonce_wrong_nonce) { |
|
35 |
+ string secret = "1234secret"; |
|
36 |
+ string nonce = UACAuth::calcNonce(secret); |
|
37 |
+ nonce+="hallo"; |
|
38 |
+ fct_chk( !UACAuth::checkNonce(nonce, secret)); |
|
39 |
+ } FCT_TEST_END(); |
|
40 |
+ |
|
41 |
+ FCT_TEST_BGN(nonce_wrong_nonce2) { |
|
42 |
+ string secret = "1234secret"; |
|
43 |
+ string nonce = UACAuth::calcNonce(secret); |
|
44 |
+ nonce[nonce.size()-1]=nonce[nonce.size()-2]; |
|
45 |
+ fct_chk( !UACAuth::checkNonce(nonce, secret)); |
|
46 |
+ } FCT_TEST_END(); |
|
47 |
+ |
|
48 |
+} FCTMF_SUITE_END(); |
... | ... |
@@ -1,4 +1,4 @@ |
1 |
-uac_auth Client authentication |
|
1 |
+uac_auth Client / Server authentication |
|
2 | 2 |
|
3 | 3 |
how to use uac_auth |
4 | 4 |
-------------------- |
... | ... |
@@ -30,3 +30,61 @@ How to |
30 | 30 |
|
31 | 31 |
see announce_auth app for example application |
32 | 32 |
|
33 |
+ |
|
34 |
+How to use server auth |
|
35 |
+---------------------- |
|
36 |
+ |
|
37 |
+To authenticate a request, use the "checkAuth" DI function. |
|
38 |
+ |
|
39 |
+ Arguments: |
|
40 |
+ args[0] - ArgObject pointing to the Request (AmSipRequest object) |
|
41 |
+ args[1] - realm |
|
42 |
+ args[2] - user |
|
43 |
+ args[3] - password |
|
44 |
+ Return values: |
|
45 |
+ ret[0] - code: 200 for successful auth, 401 for unsuccessful (ie. |
|
46 |
+ ret[1] - reason string |
|
47 |
+ ret[2] - optional auth header |
|
48 |
+ |
|
49 |
+Limitations |
|
50 |
+----------- |
|
51 |
+ |
|
52 |
+- URI in auth hdr is not checked |
|
53 |
+- multiple Authorization headers are probably not properly processed (only the first one is used) |
|
54 |
+ |
|
55 |
+code example: |
|
56 |
+ |
|
57 |
+ AmDynInvokeFactory* fact = |
|
58 |
+ AmPlugIn::instance()->getFactory4Di("uac_auth"); |
|
59 |
+ if (NULL != fact) { |
|
60 |
+ AmDynInvoke* di_inst = fact->getInstance(); |
|
61 |
+ if(di_inst) { |
|
62 |
+ AmArg di_args, di_ret; |
|
63 |
+ try { |
|
64 |
+ di_args.push(AmArg((AmObject*)&req)); |
|
65 |
+ di_args.push("myrealm"); |
|
66 |
+ di_args.push("myuser"); |
|
67 |
+ di_args.push("mypwd"); |
|
68 |
+ di_inst->invoke("checkAuth", di_args, di_ret); |
|
69 |
+ |
|
70 |
+ if (di_ret.size() >= 3) { |
|
71 |
+ if (di_ret[0].asInt() != 200) { |
|
72 |
+ DBG("Auth: replying %u %s - hdrs: '%s'\n", |
|
73 |
+ di_ret[0].asInt(), di_ret[1].asCStr(), di_ret[2].asCStr()); |
|
74 |
+ dlg->reply(req, di_ret[0].asInt(), di_ret[1].asCStr(), NULL, di_ret[2].asCStr()); |
|
75 |
+ return; |
|
76 |
+ } else { |
|
77 |
+ DBG("Successfully authenticated request.\n"); |
|
78 |
+ } |
|
79 |
+ } |
|
80 |
+ } catch (const AmDynInvoke::NotImplemented& ni) { |
|
81 |
+ ERROR("not implemented DI function 'checkAuth'\n"); |
|
82 |
+ } catch (const AmArg::OutOfBoundsException& oob) { |
|
83 |
+ ERROR("out of bounds in DI call 'checkAuth'\n"); |
|
84 |
+ } catch (const AmArg::TypeMismatchException& oob) { |
|
85 |
+ ERROR("type mismatch in DI call checkAuth\n"); |
|
86 |
+ } catch (...) { |
|
87 |
+ ERROR("unexpected Exception in DI call checkAuth\n"); |
|
88 |
+ } |
|
89 |
+ } |
|
90 |
+ } |