1 /*
2 (C) 2012, 2016 Percona LLC and/or its affiliates
3 
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; version 2 of the License.
7 
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 GNU General Public License for more details.
12 
13 You should have received a copy of the GNU General Public License
14 along with this program; if not, write to the Free Software
15 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
16 */
17 
18 #include "auth_mapping.h"
19 #include "groups.h"
20 #include <string.h>
21 #include <stdlib.h>
22 #include <ctype.h>
23 
24 #define MIN(X,Y) ((X) < (Y) ? (X) : (Y))
25 
26 /** Token representation:
27     token type, string repr, length of token */
28 struct token
29 {
30   enum { tok_id, tok_comma, tok_eq, tok_eof } token_type;
31   const char *token;
32   size_t token_len;
33 };
34 
35 /** Iterator in key-value mapping:
36     position and length of key,
37     position and length of value,
38     current position in string */
39 struct mapping_iter {
40   const char *key;
41   size_t key_len;
42   const char *value;
43   size_t value_len;
44   const char *ptr;
45 };
46 
47 /** Get next token from buf. Returns new buf position. */
get_token(struct token * token,const char * buf)48 static const char *get_token(struct token *token,
49                              const char *buf)
50 {
51   const char *ptr= buf;
52 
53   while (*ptr && isspace(*ptr))
54     ++ptr;
55 
56   token->token= ptr;
57   switch (*ptr)
58   {
59   case '\0':
60     token->token_type= tok_eof;
61     break;
62   case ',':
63     token->token_len= 1;
64     token->token_type = tok_comma;
65     ++ptr;
66     break;
67   case '=':
68     token->token_len= 1;
69     token->token_type = tok_eq;
70     ++ptr;
71     break;
72   case '"':
73     token->token_len= 0;
74     ++ptr;
75     token->token= ptr;
76     while (*ptr && *ptr != '"')
77     {
78       ++token->token_len;
79       ++ptr;
80     }
81     token->token_type= tok_id;
82     if (*ptr)
83       ++ptr;
84     break;
85   default:
86     token->token_len= 0;
87     while (*ptr && !isspace(*ptr) && *ptr != ',' && *ptr != '=')
88     {
89       ++token->token_len;
90       ++ptr;
91     }
92     token->token_type= tok_id;
93   }
94 
95   return ptr;
96 }
97 
98 /** Create iterator through mapping string.
99     Initially iterator set to position before first
100     key-value pair. On success non-NULL pointer returned, otherwise NULL */
mapping_iter_new(const char * mapping_string)101 struct mapping_iter *mapping_iter_new(const char *mapping_string)
102 {
103   struct mapping_iter *it= malloc(sizeof(struct mapping_iter));
104   struct token token;
105   if (it != NULL)
106   {
107     it->key= NULL;
108     it->value= NULL;
109     /* eat up service name and move to (, key = value)* part */
110     it->ptr= get_token(&token, mapping_string);
111   }
112   return it;
113 }
114 
115 /** Move iterator to next key-value pair.
116     On success pointer to key position in string returned,
117     otherwise NULL */
mapping_iter_next(struct mapping_iter * it)118 const char *mapping_iter_next(struct mapping_iter *it)
119 {
120   struct token token[4]= {{0, 0, 0}};
121 
122   /* read next 4 tokens */
123   it->ptr= get_token(token + 3,
124            get_token(token + 2,
125            get_token(token + 1,
126            get_token(token, it->ptr))));
127 
128   /* was it ", id = id"? */
129   if (!((token[0].token_type == tok_comma) &&
130         (token[1].token_type == tok_id) &&
131         (token[2].token_type == tok_eq) &&
132         (token[3].token_type == tok_id)))
133   {
134     /* we got something inconsistent */
135     return NULL;
136   }
137 
138   /* set key */
139   it->key= token[1].token;
140   it->key_len= token[1].token_len;
141 
142   /* set value */
143   it->value= token[3].token;
144   it->value_len= token[3].token_len;
145 
146   return it->key;
147 }
148 
149 /** Finish iteration and release iterator */
mapping_iter_free(struct mapping_iter * it)150 void mapping_iter_free(struct mapping_iter *it)
151 {
152   free(it);
153 }
154 
155 /** Get mapped value for given user name.
156     Value is looked up by using all user groups as a key.
157     Auth string is iterated only once, while groups are iterated
158     for every key-value pair. This is mean than auth string order
159     is dominant.
160 
161     Example:
162 
163     given:
164       user "foo" is the member of "wheel", "staff" and "bar".
165       auth string is "mysql, root=user1, bar=user2, staff=user3"
166 
167     result is "user2".
168 
169     On success value_buf returned, otherwise NULL */
mapping_lookup_user(const char * user_name,char * value_buf,size_t value_buf_len,const char * mapping_string)170 char *mapping_lookup_user(const char *user_name,
171                           char *value_buf, size_t value_buf_len,
172                           const char *mapping_string)
173 {
174   /* Iterate through the key-value list stored in auth_string and
175   find key (which is interpreted as group name) in the list of groups
176   for specified user. If match is found, store appropriate value in
177   the authenticated_as field. */
178   struct groups_iter *group_it;
179   struct mapping_iter *keyval_it;
180   const char *key;
181   const char *group;
182 
183   keyval_it= mapping_iter_new(mapping_string);
184   if (keyval_it == NULL)
185     return NULL;
186 
187   group_it= groups_iter_new(user_name);
188   if (group_it == NULL)
189   {
190     mapping_iter_free(keyval_it);
191     return NULL;
192   }
193 
194   while ((key= mapping_iter_next(keyval_it)) != NULL) {
195     while ((group= groups_iter_next(group_it)) != NULL) {
196       if (keyval_it->key_len == strlen(group) &&
197           strncmp(key, group, keyval_it->key_len) == 0) {
198         /* match is found */
199         memcpy(value_buf, keyval_it->value,
200                MIN(value_buf_len, keyval_it->value_len));
201         value_buf[MIN(value_buf_len, keyval_it->value_len)]= '\0';
202         groups_iter_free(group_it);
203         mapping_iter_free(keyval_it);
204         return value_buf;
205       }
206     }
207     groups_iter_reset(group_it);
208   }
209 
210   groups_iter_free(group_it);
211   mapping_iter_free(keyval_it);
212 
213   return NULL;
214 }
215 
216 /** Get key in current iterator pos. On success buf returned,
217     otherwise NULL */
mapping_iter_get_key(struct mapping_iter * it,char * buf,size_t buf_len)218 char *mapping_iter_get_key(struct mapping_iter *it, char *buf, size_t buf_len)
219 {
220   if (it->key == NULL)
221     return NULL;
222   memcpy(buf, it->key, MIN(buf_len, it->key_len));
223   buf[MIN(buf_len, it->key_len)]= '\0';
224   return buf;
225 }
226 
227 /** Get value in current iterator pos. On success buf returned,
228     otherwise NULL */
mapping_iter_get_value(struct mapping_iter * it,char * buf,size_t buf_len)229 char *mapping_iter_get_value(struct mapping_iter *it, char *buf, size_t buf_len)
230 {
231   if (it->value == NULL)
232     return NULL;
233   memcpy(buf, it->value, MIN(buf_len, it->value_len));
234   buf[MIN(buf_len, it->value_len)]= '\0';
235   return buf;
236 }
237 
238 /** Get value by key. On success pointer to service_name
239     returned, otherwise NULL */
mapping_get_service_name(char * buf,size_t buf_len,const char * mapping_string)240 char *mapping_get_service_name(char *buf, size_t buf_len,
241                                const char *mapping_string)
242 {
243   struct token token;
244 
245   get_token(&token, mapping_string);
246   if (token.token_type == tok_id)
247   {
248     memcpy(buf, token.token, MIN(buf_len, token.token_len));
249     buf[MIN(buf_len, token.token_len)]= '\0';
250     return buf;
251   }
252 
253   return NULL;
254 }
255