File: digest.c

package info (click to toggle)
openser 1.1.0-9etch1
  • links: PTS
  • area: main
  • in suites: etch, etch-m68k
  • size: 9,828 kB
  • ctags: 11,809
  • sloc: ansic: 120,528; sh: 5,249; yacc: 1,716; makefile: 1,261; php: 656; perl: 205; sql: 190
file content (235 lines) | stat: -rw-r--r-- 6,476 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
/*
 * $Id: digest.c,v 1.3 2006/03/29 19:10:14 juhe Exp $
 *
 * Digest credentials parser interface
 *
 * Copyright (C) 2001-2003 FhG Fokus
 *
 * This file is part of openser, a free SIP server.
 *
 * openser is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version
 *
 * openser is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License 
 * along with this program; if not, write to the Free Software 
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */


#include "digest.h"
#include "../../mem/mem.h"  /* pkg_malloc */
#include "../../dprint.h"   /* Guess what */
#include <stdio.h>          /* printf */
#include <string.h>         /* strncasecmp */


/*
 * Create and initialize a new credentials structure
 */
static inline int new_credentials(struct hdr_field* _h)
{
	auth_body_t* b;

	b = (auth_body_t*)pkg_malloc(sizeof(auth_body_t));
	if (b == 0) {
		LOG(L_ERR, "parse_credentials(): No memory left\n");
		return -1;
	}
		
	init_dig_cred(&(b->digest));
	b->stale = 0;
	b->authorized = 0;

	_h->parsed = (void*)b;

	return 0;
}


/*
 * Parse digest credentials
 * Return value -1 means that the function was unable to allocate
 * memory and therefore the server should return Internal Server Error,
 * not Bad Request in this case !
 * Bad Request should be send when return value != -1
 */
int parse_credentials(struct hdr_field* _h)
{
	int res;

	if (_h->parsed) {
		return 0;  /* Already parsed */
	}

	if (new_credentials(_h) < 0) {
		LOG(L_ERR, "parse_credentials(): Can't create new credentials\n");
		return -1;
	}

	     /* parse_digest_cred must return < -1 on error otherwise we will be
	      * unable to distinguish if the error was caused by the server or if the
	      * credentials are broken
	      */
	res = parse_digest_cred(&(_h->body), &(((auth_body_t*)(_h->parsed))->digest));
	
	if (res != 0) {
		free_credentials((auth_body_t**)&(_h->parsed));
	}

	return res;
}


/*
 * Free all memory
 */
void free_credentials(auth_body_t** _b)
{
	pkg_free(*_b);
	*_b = 0;
}


/*
 * Check semantics of a digest credentials structure
 * Make sure that all attributes needed to verify response 
 * string are set or at least have a default value
 *
 * The returned value is logical OR of all errors encountered
 * during the check, see dig_err_t type for more details 
 */
dig_err_t check_dig_cred(dig_cred_t* _c)
{
	dig_err_t res = E_DIG_OK;

	     /* Username must be present */
	if (_c->username.user.s == 0) res |= E_DIG_USERNAME;

	     /* Realm must be present */
	if (_c->realm.s == 0)  res |= E_DIG_REALM;

	     /* If Username has domain, it must equal to Realm */
	if (_c->username.domain.s && 
	    ((_c->username.domain.len != _c->realm.len) ||
	     (strncmp(_c->username.domain.s, _c->realm.s,
		      _c->realm.len) != 0)))
	    res |= E_DIG_DOMAIN;

	     /* Nonce that was used must be specified */
	if (_c->nonce.s == 0) res |= E_DIG_NONCE;

	     /* URI must be specified */
	if (_c->uri.s == 0) res |= E_DIG_URI;

	     /* We cannot check credentials without response */
	if (_c->response.s == 0) res |= E_DIG_RESPONSE;

	     /* If QOP parameter is present, some additional
	      * requirements must be met
	      */
	if ((_c->qop.qop_parsed == QOP_AUTH) || (_c->qop.qop_parsed == QOP_AUTHINT)) {
		     /* CNONCE must be specified */
		if (_c->cnonce.s == 0) res |= E_DIG_CNONCE;
		     /* and also nonce count must be specified */
		if (_c->nc.s == 0) res |= E_DIG_NC;
	}
		
	return res;	
}


/*
 * Print credential structure content to stdout
 * Just for debugging
 */
void print_cred(dig_cred_t* _c)
{
	printf("===Digest credentials===\n");
	if (_c) {
		printf("Username\n");
		printf("+--whole  = \'%.*s\'\n", _c->username.whole.len, _c->username.whole.s);
		printf("+--user   = \'%.*s\'\n", _c->username.user.len, _c->username.user.s);
		printf("\\--domain = \'%.*s\'\n", _c->username.domain.len, _c->username.domain.s);
		printf("Realm     = \'%.*s\'\n", _c->realm.len, _c->realm.s);
		printf("Nonce     = \'%.*s\'\n", _c->nonce.len, _c->nonce.s);
		printf("URI       = \'%.*s\'\n", _c->uri.len, _c->uri.s);
		printf("Response  = \'%.*s\'\n", _c->response.len, _c->response.s);
		printf("Algorithm = \'%.*s\'\n", _c->alg.alg_str.len, _c->alg.alg_str.s);
		printf("\\--parsed = ");

		switch(_c->alg.alg_parsed) {
		case ALG_UNSPEC:  printf("ALG_UNSPEC\n");  break;
		case ALG_MD5:     printf("ALG_MD5\n");     break;
		case ALG_MD5SESS: printf("ALG_MD5SESS\n"); break;
		case ALG_OTHER:   printf("ALG_OTHER\n");   break;
		}

		printf("Cnonce    = \'%.*s\'\n", _c->cnonce.len, _c->cnonce.s);
		printf("Opaque    = \'%.*s\'\n", _c->opaque.len, _c->opaque.s);
		printf("QOP       = \'%.*s\'\n", _c->qop.qop_str.len, _c->qop.qop_str.s);
		printf("\\--parsed = ");

		switch(_c->qop.qop_parsed) {
		case QOP_UNSPEC:  printf("QOP_UNSPEC\n");  break;
		case QOP_AUTH:    printf("QOP_AUTH\n");    break;
		case QOP_AUTHINT: printf("QOP_AUTHINT\n"); break;
		case QOP_OTHER:   printf("QOP_OTHER\n");   break;
		}
		printf("NC        = \'%.*s\'\n", _c->nc.len, _c->nc.s);
	}
	printf("===/Digest credentials===\n");
}


/*
 * Mark credentials as selected so functions
 * following authorize know which credentials
 * to use if the message contained more than
 * one
 */
int mark_authorized_cred(struct sip_msg* _m, struct hdr_field* _h)
{
	struct hdr_field* f;
	
	switch(_h->type) {
	case HDR_AUTHORIZATION_T: f = _m->authorization; break;
	case HDR_PROXYAUTH_T:     f = _m->proxy_auth;    break;
	default:
		LOG(L_ERR, "mark_authorized_cred(): Invalid header field type\n");
		return -1;
	}

	if (!(f->parsed)) {
		if (new_credentials(f) < 0) {
			LOG(L_ERR, "mark_authorized_cred(): Error in new_credentials\n");
			return -1;
		}
	}

	((auth_body_t*)(f->parsed))->authorized = _h;

	return 0;
}


/*
 * Get pointer to authorized credentials, if there are no
 * authorized credentials, 0 is returned
 */
int get_authorized_cred(struct hdr_field* _f, struct hdr_field** _h)
{
	if (_f && _f->parsed) {
		*_h = ((auth_body_t*)(_f->parsed))->authorized;
	} else {
		*_h = 0;
	}
	
	return 0;
}