modules/ims_qos: added patch for flow-description bug when request originates from...
[sip-router] / parser / digest / digest.c
1 /*
2  * Digest credentials parser interface
3  *
4  * Copyright (C) 2001-2003 FhG Fokus
5  *
6  * This file is part of ser, a free SIP server.
7  *
8  * ser is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version
12  *
13  * For a license to use the ser software under conditions
14  * other than those described here, or to purchase support for this
15  * software, please contact iptel.org by e-mail at the following addresses:
16  *    info@iptel.org
17  *
18  * ser is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License 
24  * along with this program; if not, write to the Free Software 
25  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
26  */
27
28
29 #include "digest.h"
30 #include "../../mem/mem.h"  /* pkg_malloc */
31 #include "../../dprint.h"   /* Guess what */
32 #include <stdio.h>          /* printf */
33 #include <string.h>         /* strncasecmp */
34
35
36 /*
37  * Create and initialize a new credentials structure
38  */
39 static inline int new_credentials(struct hdr_field* _h)
40 {
41         auth_body_t* b;
42
43         b = (auth_body_t*)pkg_malloc(sizeof(auth_body_t));
44         if (b == 0) {
45                 LOG(L_ERR, "parse_credentials(): No memory left\n");
46                 return -1;
47         }
48                 
49         init_dig_cred(&(b->digest));
50         b->stale = 0;
51         b->authorized = 0;
52
53         _h->parsed = (void*)b;
54
55         return 0;
56 }
57
58
59 /*
60  * Parse digest credentials
61  * Return value -1 means that the function was unable to allocate
62  * memory and therefore the server should return Internal Server Error,
63  * not Bad Request in this case !
64  * Bad Request should be send when return value != -1
65  */
66 int parse_credentials(struct hdr_field* _h)
67 {
68         int res;
69         void** ph_parsed;
70
71         if (_h->parsed) {
72                 return 0;  /* Already parsed */
73         }
74
75         if (new_credentials(_h) < 0) {
76                 LOG(L_ERR, "parse_credentials(): Can't create new credentials\n");
77                 return -1;
78         }
79
80              /* parse_digest_cred must return < -1 on error otherwise we will be
81               * unable to distinguish if the error was caused by the server or if the
82               * credentials are broken
83               */
84         res = parse_digest_cred(&(_h->body), &(((auth_body_t*)(_h->parsed))->digest));
85         
86         if (res != 0) {
87                 ph_parsed=&_h->parsed;
88                 free_credentials((auth_body_t**)ph_parsed);
89         }
90
91         return res;
92 }
93
94
95 /*
96  * Free all memory
97  */
98 void free_credentials(auth_body_t** _b)
99 {
100         pkg_free(*_b);
101         *_b = 0;
102 }
103
104
105 /*
106  * Check semantics of a digest credentials structure
107  * Make sure that all attributes needed to verify response 
108  * string are set or at least have a default value
109  *
110  * The returned value is logical OR of all errors encountered
111  * during the check, see dig_err_t type for more details 
112  */
113 dig_err_t check_dig_cred(dig_cred_t* _c)
114 {
115         dig_err_t res = E_DIG_OK;
116
117              /* Username must be present */
118         if (_c->username.user.s == 0) res |= E_DIG_USERNAME;
119
120              /* Realm must be present */
121         if (_c->realm.s == 0)  res |= E_DIG_REALM;
122
123              /* Nonce that was used must be specified */
124         if (_c->nonce.s == 0) res |= E_DIG_NONCE;
125
126              /* URI must be specified */
127         if (_c->uri.s == 0) res |= E_DIG_URI;
128
129              /* We cannot check credentials without response */
130         if (_c->response.s == 0) res |= E_DIG_RESPONSE;
131
132              /* If QOP parameter is present, some additional
133               * requirements must be met
134               */
135         if ((_c->qop.qop_parsed == QOP_AUTH) || (_c->qop.qop_parsed == QOP_AUTHINT)) {
136                      /* CNONCE must be specified */
137                 if (_c->cnonce.s == 0) res |= E_DIG_CNONCE;
138                      /* and also nonce count must be specified */
139                 if (_c->nc.s == 0) res |= E_DIG_NC;
140         }
141                 
142         return res;     
143 }
144
145
146 /*
147  * Print credential structure content to stdout
148  * Just for debugging
149  */
150 void print_cred(dig_cred_t* _c)
151 {
152         printf("===Digest credentials===\n");
153         if (_c) {
154                 printf("Username\n");
155                 printf("+--whole  = \'%.*s\'\n", _c->username.whole.len, _c->username.whole.s);
156                 printf("+--user   = \'%.*s\'\n", _c->username.user.len, _c->username.user.s);
157                 printf("\\--domain = \'%.*s\'\n", _c->username.domain.len, _c->username.domain.s);
158                 printf("Realm     = \'%.*s\'\n", _c->realm.len, _c->realm.s);
159                 printf("Nonce     = \'%.*s\'\n", _c->nonce.len, _c->nonce.s);
160                 printf("URI       = \'%.*s\'\n", _c->uri.len, _c->uri.s);
161                 printf("Response  = \'%.*s\'\n", _c->response.len, _c->response.s);
162                 printf("Algorithm = \'%.*s\'\n", _c->alg.alg_str.len, _c->alg.alg_str.s);
163                 printf("\\--parsed = ");
164
165                 switch(_c->alg.alg_parsed) {
166                 case ALG_UNSPEC:  printf("ALG_UNSPEC\n");  break;
167                 case ALG_MD5:     printf("ALG_MD5\n");     break;
168                 case ALG_MD5SESS: printf("ALG_MD5SESS\n"); break;
169                 case ALG_OTHER:   printf("ALG_OTHER\n");   break;
170                 }
171
172                 printf("Cnonce    = \'%.*s\'\n", _c->cnonce.len, _c->cnonce.s);
173                 printf("Opaque    = \'%.*s\'\n", _c->opaque.len, _c->opaque.s);
174                 printf("QOP       = \'%.*s\'\n", _c->qop.qop_str.len, _c->qop.qop_str.s);
175                 printf("\\--parsed = ");
176
177                 switch(_c->qop.qop_parsed) {
178                 case QOP_UNSPEC:  printf("QOP_UNSPEC\n");  break;
179                 case QOP_AUTH:    printf("QOP_AUTH\n");    break;
180                 case QOP_AUTHINT: printf("QOP_AUTHINT\n"); break;
181                 case QOP_OTHER:   printf("QOP_OTHER\n");   break;
182                 }
183                 printf("NC        = \'%.*s\'\n", _c->nc.len, _c->nc.s);
184         }
185         printf("===/Digest credentials===\n");
186 }
187
188
189 /*
190  * Mark credentials as selected so functions
191  * following authorize know which credentials
192  * to use if the message contained more than
193  * one
194  */
195 int mark_authorized_cred(struct sip_msg* _m, struct hdr_field* _h)
196 {
197         struct hdr_field* f;
198         
199         switch(_h->type) {
200         case HDR_AUTHORIZATION_T: f = _m->authorization; break;
201         case HDR_PROXYAUTH_T:     f = _m->proxy_auth;    break;
202         default:
203                 LOG(L_ERR, "mark_authorized_cred(): Invalid header field type\n");
204                 return -1;
205         }
206
207         if (!(f->parsed)) {
208                 if (new_credentials(f) < 0) {
209                         LOG(L_ERR, "mark_authorized_cred(): Error in new_credentials\n");
210                         return -1;
211                 }
212         }
213
214         ((auth_body_t*)(f->parsed))->authorized = _h;
215
216         return 0;
217 }
218
219
220 /*
221  * Get pointer to authorized credentials, if there are no
222  * authorized credentials, 0 is returned
223  */
224 int get_authorized_cred(struct hdr_field* _f, struct hdr_field** _h)
225 {
226         if (_f && _f->parsed) {
227                 *_h = ((auth_body_t*)(_f->parsed))->authorized;
228         } else {
229                 *_h = 0;
230         }
231         
232         return 0;
233 }
234
235
236 /*
237  * Find credentials with given realm in a SIP message header
238  */
239 int find_credentials(struct sip_msg* msg, str* realm,
240                      hdr_types_t hftype, struct hdr_field** hdr)
241 {
242         struct hdr_field** hook, *ptr;
243         hdr_flags_t hdr_flags;
244         int res;
245         str* r;
246
247              /*
248               * Determine if we should use WWW-Authorization or
249               * Proxy-Authorization header fields, this parameter
250               * is set in www_authorize and proxy_authorize
251               */
252         switch(hftype) {
253         case HDR_AUTHORIZATION_T: 
254                 hook = &(msg->authorization);
255                 hdr_flags=HDR_AUTHORIZATION_F;
256                 break;
257         case HDR_PROXYAUTH_T:
258                 hook = &(msg->proxy_auth);
259                 hdr_flags=HDR_PROXYAUTH_F;
260                 break;
261         default:                                
262                 hook = &(msg->authorization);
263                 hdr_flags=HDR_T2F(hftype);
264                 break;
265         }
266
267              /*
268               * If the credentials haven't been parsed yet, do it now
269               */
270         if (*hook == 0) {
271                      /* No credentials parsed yet */
272                 if (parse_headers(msg, hdr_flags, 0) == -1) {
273                         LOG(L_ERR, "auth:find_credentials: Error while parsing headers\n");
274                         return -1;
275                 }
276         }
277
278         ptr = *hook;
279
280              /*
281               * Iterate through the credentials in the message and
282               * find credentials with given realm
283               */
284         while(ptr) {
285                 res = parse_credentials(ptr);
286                 if (res < 0) {
287                         LOG(L_ERR, "auth:find_credentials: Error while parsing credentials\n");
288                         return (res == -1) ? -2 : -3;
289                 } else if (res == 0) {
290                         r = &(((auth_body_t*)(ptr->parsed))->digest.realm);
291
292                         if (r->len == realm->len) {
293                                 if (!strncasecmp(realm->s, r->s, r->len)) {
294                                         *hdr = ptr;
295                                         return 0;
296                                 }
297                         }
298                 }
299
300                 if (parse_headers(msg, hdr_flags, 1) == -1) {
301                         LOG(L_ERR, "auth:find_credentials: Error while parsing headers\n");
302                         return -4;
303                 } else {
304                         ptr = next_sibling_hdr(ptr);
305                         if (!ptr)
306                                 break;
307                 }
308         }
309         
310              /*
311               * Credentials with given realm not found
312               */
313         return 1;
314 }