1/* mechtools.c --- Helper functions available for use by any mechanism.
2 * Copyright (C) 2010-2012 Simon Josefsson
3 *
4 * This file is part of GNU SASL Library.
5 *
6 * GNU SASL Library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public License
8 * as published by the Free Software Foundation; either version 2.1 of
9 * the License, or (at your option) any later version.
10 *
11 * GNU SASL Library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with GNU SASL Library; if not, write to the Free
18 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 *
21 */
22
23#ifdef HAVE_CONFIG_H
24#include "config.h"
25#endif
26
27/* Get specification. */
28#include "mechtools.h"
29
30/* Get strcmp. */
31#include <string.h>
32
33/* Get malloc, free. */
34#include <stdlib.h>
35
36/* Get asprintf. */
37#include <stdio.h>
38
39/* Get error codes. */
40#include <gsasl.h>
41
42/* Create in AUTHZID a newly allocated copy of STR where =2C is
43 replaced with , and =3D is replaced with =. Return GSASL_OK on
44 success, GSASL_MALLOC_ERROR on memory errors, GSASL_PARSE_ERRORS if
45 string contains any unencoded ',' or incorrectly encoded
46 sequence. */
47static int
48unescape_authzid (const char *str, size_t len, char **authzid)
49{
50 char *p;
51
52 if (memchr (str, ',', len) != NULL)
53 return GSASL_MECHANISM_PARSE_ERROR;
54
55 p = *authzid = malloc (len + 1);
56 if (!p)
57 return GSASL_MALLOC_ERROR;
58
59 while (len > 0 && *str)
60 {
61 if (len >= 3 && str[0] == '=' && str[1] == '2' && str[2] == 'C')
62 {
63 *p++ = ',';
64 str += 3;
65 len -= 3;
66 }
67 else if (len >= 3 && str[0] == '=' && str[1] == '3' && str[2] == 'D')
68 {
69 *p++ = '=';
70 str += 3;
71 len -= 3;
72 }
73 else if (str[0] == '=')
74 {
75 free (*authzid);
76 *authzid = NULL;
77 return GSASL_MECHANISM_PARSE_ERROR;
78 }
79 else
80 {
81 *p++ = *str;
82 str++;
83 len--;
84 }
85 }
86 *p = '\0';
87
88 return GSASL_OK;
89}
90
91/* Parse the GS2 header containing flags and authorization identity.
92 Put authorization identity (or NULL) in AUTHZID and length of
93 header in HEADERLEN. Return GSASL_OK on success or an error
94 code.*/
95int
96_gsasl_parse_gs2_header (const char *data, size_t len,
97 char **authzid, size_t * headerlen)
98{
99 char *authzid_endptr;
100
101 if (len < 3)
102 return GSASL_MECHANISM_PARSE_ERROR;
103
104 if (strncmp (data, "n,,", 3) == 0)
105 {
106 *headerlen = 3;
107 *authzid = NULL;
108 }
109 else if (strncmp (data, "n,a=", 4) == 0 &&
110 (authzid_endptr = memchr (data + 4, ',', len - 4)))
111 {
112 int res;
113
114 if (authzid_endptr == NULL)
115 return GSASL_MECHANISM_PARSE_ERROR;
116
117 res = unescape_authzid (data + 4, authzid_endptr - (data + 4), authzid);
118 if (res != GSASL_OK)
119 return res;
120
121 *headerlen = authzid_endptr - data + 1;
122 }
123 else
124 return GSASL_MECHANISM_PARSE_ERROR;
125
126 return GSASL_OK;
127}
128
129/* Return newly allocated copy of STR with all occurrences of ','
130 replaced with =2C and '=' with '=3D', or return NULL on memory
131 allocation errors. */
132static char *
133escape_authzid (const char *str)
134{
135 char *out = malloc (strlen (str) * 3 + 1);
136 char *p = out;
137
138 if (!out)
139 return NULL;
140
141 while (*str)
142 {
143 if (*str == ',')
144 {
145 memcpy (p, "=2C", 3);
146 p += 3;
147 }
148 else if (*str == '=')
149 {
150 memcpy (p, "=3D", 3);
151 p += 3;
152 }
153 else
154 {
155 *p = *str;
156 p++;
157 }
158 str++;
159 }
160 *p = '\0';
161
162 return out;
163}
164
165/* Generate a newly allocated GS2 header, escaping authzid
166 appropriately, and appending EXTRA. */
167int
168_gsasl_gs2_generate_header (bool nonstd, char cbflag,
169 const char *cbname, const char *authzid,
170 size_t extralen, const char *extra,
171 char **gs2h, size_t * gs2hlen)
172{
173 int elen = extralen;
174 char *gs2cbflag;
175 int len;
176
177 if (cbflag == 'p')
178 len = asprintf (&gs2cbflag, "p=%s", cbname);
179 else if (cbflag == 'n')
180 len = asprintf (&gs2cbflag, "n");
181 else if (cbflag == 'y')
182 len = asprintf (&gs2cbflag, "y");
183 else
184 /* internal caller error */
185 return GSASL_MECHANISM_PARSE_ERROR;
186
187 if (len <= 0 || gs2cbflag == NULL)
188 return GSASL_MALLOC_ERROR;
189
190 if (authzid)
191 {
192 char *escaped_authzid = escape_authzid (authzid);
193
194 if (!escaped_authzid)
195 {
196 free (gs2cbflag);
197 return GSASL_MALLOC_ERROR;
198 }
199
200 len = asprintf (gs2h, "%s%s,a=%s,%.*s", nonstd ? "F," : "",
201 gs2cbflag, escaped_authzid, elen, extra);
202
203 free (escaped_authzid);
204 }
205 else
206 len = asprintf (gs2h, "%s%s,,%.*s", nonstd ? "F," : "", gs2cbflag,
207 elen, extra);
208
209 free (gs2cbflag);
210
211 if (len <= 0 || gs2h == NULL)
212 return GSASL_MALLOC_ERROR;
213
214 *gs2hlen = len;
215
216 return GSASL_OK;
217}
218