1 /***
2 This file is part of PulseAudio.
3
4 Copyright 2008 Colin Guthrie
5 Copyright Kungliga Tekniska högskolan
6 Copyright 2013 Martin Blanchard
7
8 PulseAudio is free software; you can redistribute it and/or modify
9 it under the terms of the GNU Lesser General Public License as published
10 by the Free Software Foundation; either version 2.1 of the License,
11 or (at your option) any later version.
12
13 PulseAudio is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 /***
23 The base64 implementation was originally inspired by a file developed
24 by Kungliga Tekniska högskolan.
25 ***/
26
27 #ifdef HAVE_CONFIG_H
28 #include <config.h>
29 #endif
30
31 #include <stdlib.h>
32 #include <string.h>
33
34 #include <openssl/err.h>
35 #include <openssl/md5.h>
36
37 #include <pulse/xmalloc.h>
38
39 #include <pulsecore/core-util.h>
40 #include <pulsecore/macro.h>
41
42 #include "raop-util.h"
43
44 #ifndef MD5_DIGEST_LENGTH
45 #define MD5_DIGEST_LENGTH 16
46 #endif
47
48 #define MD5_HASH_LENGTH (2*MD5_DIGEST_LENGTH)
49
50 #define BASE64_DECODE_ERROR 0xffffffff
51
52 static const char base64_chars[] =
53 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
54
char_position(char c)55 static int char_position(char c) {
56 if (c >= 'A' && c <= 'Z')
57 return c - 'A' + 0;
58 if (c >= 'a' && c <= 'z')
59 return c - 'a' + 26;
60 if (c >= '0' && c <= '9')
61 return c - '0' + 52;
62 if (c == '+')
63 return 62;
64 if (c == '/')
65 return 63;
66
67 return -1;
68 }
69
token_decode(const char * token)70 static unsigned int token_decode(const char *token) {
71 unsigned int val = 0;
72 int marker = 0;
73 int i;
74
75 if (strlen(token) < 4)
76 return BASE64_DECODE_ERROR;
77 for (i = 0; i < 4; i++) {
78 val *= 64;
79 if (token[i] == '=')
80 marker++;
81 else if (marker > 0)
82 return BASE64_DECODE_ERROR;
83 else {
84 int lpos = char_position(token[i]);
85 if (lpos < 0)
86 return BASE64_DECODE_ERROR;
87 val += lpos;
88 }
89 }
90
91 if (marker > 2)
92 return BASE64_DECODE_ERROR;
93
94 return (marker << 24) | val;
95 }
96
pa_raop_base64_encode(const void * data,int len,char ** str)97 int pa_raop_base64_encode(const void *data, int len, char **str) {
98 const unsigned char *q;
99 char *p, *s = NULL;
100 int i, c;
101
102 pa_assert(data);
103 pa_assert(str);
104
105 p = s = pa_xnew(char, len * 4 / 3 + 4);
106 q = (const unsigned char *) data;
107 for (i = 0; i < len;) {
108 c = q[i++];
109 c *= 256;
110 if (i < len)
111 c += q[i];
112 i++;
113 c *= 256;
114 if (i < len)
115 c += q[i];
116 i++;
117 p[0] = base64_chars[(c & 0x00fc0000) >> 18];
118 p[1] = base64_chars[(c & 0x0003f000) >> 12];
119 p[2] = base64_chars[(c & 0x00000fc0) >> 6];
120 p[3] = base64_chars[(c & 0x0000003f) >> 0];
121 if (i > len)
122 p[3] = '=';
123 if (i > len + 1)
124 p[2] = '=';
125 p += 4;
126 }
127
128 *p = 0;
129 *str = s;
130 return strlen(s);
131 }
132
pa_raop_base64_decode(const char * str,void * data)133 int pa_raop_base64_decode(const char *str, void *data) {
134 const char *p;
135 unsigned char *q;
136
137 pa_assert(str);
138 pa_assert(data);
139
140 q = data;
141 for (p = str; *p && (*p == '=' || strchr(base64_chars, *p)); p += 4) {
142 unsigned int val = token_decode(p);
143 unsigned int marker = (val >> 24) & 0xff;
144 if (val == BASE64_DECODE_ERROR)
145 return -1;
146 *q++ = (val >> 16) & 0xff;
147 if (marker < 2)
148 *q++ = (val >> 8) & 0xff;
149 if (marker < 1)
150 *q++ = val & 0xff;
151 }
152
153 return q - (unsigned char *) data;
154 }
155
pa_raop_md5_hash(const char * data,int len,char ** str)156 int pa_raop_md5_hash(const char *data, int len, char **str) {
157 unsigned char d[MD5_DIGEST_LENGTH];
158 char *s = NULL;
159 int i;
160
161 pa_assert(data);
162 pa_assert(str);
163
164 MD5((unsigned char*) data, len, d);
165 s = pa_xnew(char, MD5_HASH_LENGTH);
166 for (i = 0; i < MD5_DIGEST_LENGTH; i++)
167 sprintf(&s[2*i], "%02x", (unsigned int) d[i]);
168
169 *str = s;
170 s[MD5_HASH_LENGTH] = 0;
171 return strlen(s);
172 }
173
pa_raop_basic_response(const char * user,const char * pwd,char ** str)174 int pa_raop_basic_response(const char *user, const char *pwd, char **str) {
175 char *tmp, *B = NULL;
176
177 pa_assert(str);
178
179 tmp = pa_sprintf_malloc("%s:%s", user, pwd);
180 pa_raop_base64_encode(tmp, strlen(tmp), &B);
181 pa_xfree(tmp);
182
183 *str = B;
184 return strlen(B);
185 }
186
pa_raop_digest_response(const char * user,const char * realm,const char * password,const char * nonce,const char * uri,char ** str)187 int pa_raop_digest_response(const char *user, const char *realm, const char *password,
188 const char *nonce, const char *uri, char **str) {
189 char *A1, *HA1, *A2, *HA2;
190 char *tmp, *KD = NULL;
191
192 pa_assert(str);
193
194 A1 = pa_sprintf_malloc("%s:%s:%s", user, realm, password);
195 pa_raop_md5_hash(A1, strlen(A1), &HA1);
196 pa_xfree(A1);
197
198 A2 = pa_sprintf_malloc("OPTIONS:%s", uri);
199 pa_raop_md5_hash(A2, strlen(A2), &HA2);
200 pa_xfree(A2);
201
202 tmp = pa_sprintf_malloc("%s:%s:%s", HA1, nonce, HA2);
203 pa_raop_md5_hash(tmp, strlen(tmp), &KD);
204 pa_xfree(tmp);
205
206 pa_xfree(HA1);
207 pa_xfree(HA2);
208
209 *str = KD;
210 return strlen(KD);
211 }
212