• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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