Use constants instead of hardcoded numbers
[freeradius.git] / src / modules / rlm_soh / rlm_soh.c
1 /*
2  *   This program is is free software; you can redistribute it and/or modify
3  *   it under the terms of the GNU General Public License as published by
4  *   the Free Software Foundation; either version 2 of the License, or (at
5  *   your option) any later version.
6  *
7  *   This program is distributed in the hope that it will be useful,
8  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
9  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  *   GNU General Public License for more details.
11  *
12  *   You should have received a copy of the GNU General Public License
13  *   along with this program; if not, write to the Free Software
14  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15  */
16
17 /**
18  * $Id$
19  * @file rlm_soh.c
20  * @brief Decodes Microsoft's Statement of Health sub-protocol.
21  *
22  * @copyright 2010 Phil Mayers <p.mayers@imperial.ac.uk>
23  */
24 RCSID("$Id$")
25
26 #include        <freeradius-devel/radiusd.h>
27 #include        <freeradius-devel/modules.h>
28 #include        <freeradius-devel/dhcp.h>
29 #include        <freeradius-devel/soh.h>
30
31
32 typedef struct rlm_soh_t {
33         char const *xlat_name;
34         bool dhcp;
35 } rlm_soh_t;
36
37
38 /*
39  * Not sure how to make this useful yet...
40  */
41 static ssize_t soh_xlat(UNUSED void *instance, REQUEST *request, char const *fmt, char *out, size_t outlen) {
42
43         VALUE_PAIR* vp[6];
44         char const *osname;
45
46         /*
47          * There will be no point unless SoH-Supported = yes
48          */
49         vp[0] = pairfind(request->packet->vps, PW_SOH_SUPPORTED, 0, TAG_ANY);
50         if (!vp[0])
51                 return 0;
52
53
54         if (strncasecmp(fmt, "OS", 2) == 0) {
55                 /* OS vendor */
56                 vp[0] = pairfind(request->packet->vps, PW_SOH_MS_MACHINE_OS_VENDOR,  0, TAG_ANY);
57                 vp[1] = pairfind(request->packet->vps, PW_SOH_MS_MACHINE_OS_VERSION, 0, TAG_ANY);
58                 vp[2] = pairfind(request->packet->vps, PW_SOH_MS_MACHINE_OS_RELEASE, 0, TAG_ANY);
59                 vp[3] = pairfind(request->packet->vps, PW_SOH_MS_MACHINE_OS_BUILD,   0, TAG_ANY);
60                 vp[4] = pairfind(request->packet->vps, PW_SOH_MS_MACHINE_SP_VERSION, 0, TAG_ANY);
61                 vp[5] = pairfind(request->packet->vps, PW_SOH_MS_MACHINE_SP_RELEASE, 0, TAG_ANY);
62
63                 if (vp[0] && vp[0]->vp_integer == VENDORPEC_MICROSOFT) {
64                         if (!vp[1]) {
65                                 snprintf(out, outlen, "Windows unknown");
66                         } else {
67                                 switch (vp[1]->vp_integer) {
68                                 case 7:
69                                         osname = "7";
70                                         break;
71
72                                 case 6:
73                                         osname = "Vista";
74                                         break;
75
76                                 case 5:
77                                         osname = "XP";
78                                         break;
79
80                                 default:
81                                         osname = "Other";
82                                         break;
83                                 }
84                                 snprintf(out, outlen, "Windows %s %d.%d.%d sp %d.%d", osname, vp[1]->vp_integer,
85                                                 vp[2] ? vp[2]->vp_integer : 0,
86                                                 vp[3] ? vp[3]->vp_integer : 0,
87                                                 vp[4] ? vp[4]->vp_integer : 0,
88                                                 vp[5] ? vp[5]->vp_integer : 0
89                                         );
90                         }
91                         return strlen(out);
92                 }
93         }
94
95         return 0;
96 }
97
98
99 static const CONF_PARSER module_config[] = {
100         /*
101          * Do SoH over DHCP?
102          */
103         { "dhcp", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_soh_t, dhcp), "no" },
104
105         { NULL, -1, 0, NULL, NULL }             /* end the list */
106 };
107
108
109 static int mod_instantiate(CONF_SECTION *conf, void *instance)
110 {
111         char const *name;
112         rlm_soh_t *inst = instance;
113
114         name = cf_section_name2(conf);
115         if (!name) name = cf_section_name1(conf);
116         inst->xlat_name = name;
117         if (!inst->xlat_name) return -1;
118         xlat_register(inst->xlat_name, soh_xlat, NULL, inst);
119
120         return 0;
121 }
122
123 static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *request)
124 {
125 #ifdef WITH_DHCP
126         int rcode;
127         VALUE_PAIR *vp;
128         rlm_soh_t *inst = instance;
129
130         if (!inst->dhcp) return RLM_MODULE_NOOP;
131
132         vp = pairfind(request->packet->vps, 43, DHCP_MAGIC_VENDOR, TAG_ANY);
133         if (vp) {
134                 /*
135                  * vendor-specific options contain
136                  *
137                  * vendor opt 220/0xdc - SoH payload, or null byte to probe, or string
138                  * "NAP" to indicate server-side support for SoH in OFFERs
139                  *
140                  * vendor opt 222/0xde - SoH correlation ID as utf-16 string, yuck...
141                  */
142                 uint8_t vopt, vlen;
143                 uint8_t const *data;
144
145                 data = vp->vp_octets;
146                 while (data < vp->vp_octets + vp->vp_length) {
147                         vopt = *data++;
148                         vlen = *data++;
149                         switch (vopt) {
150                         case 220:
151                                 if (vlen <= 1) {
152                                         uint8_t *p;
153
154                                         RDEBUG("SoH adding NAP marker to DHCP reply");
155                                         /* client probe; send "NAP" in the reply */
156                                         vp = paircreate(request->reply, 43, DHCP_MAGIC_VENDOR);
157                                         vp->vp_length = 5;
158                                         vp->vp_octets = p = talloc_array(vp, uint8_t, vp->vp_length);
159
160                                         p[0] = 220;
161                                         p[1] = 3;
162                                         p[4] = 'N';
163                                         p[3] = 'A';
164                                         p[2] = 'P';
165
166                                         pairadd(&request->reply->vps, vp);
167
168                                 } else {
169                                         RDEBUG("SoH decoding NAP from DHCP request");
170                                         /* SoH payload */
171                                         rcode = soh_verify(request, data, vlen);
172                                         if (rcode < 0) {
173                                                 return RLM_MODULE_FAIL;
174                                         }
175                                 }
176                                 break;
177
178                         default:
179                                 /* nothing to do */
180                                 break;
181                         }
182                         data += vlen;
183                 }
184                 return RLM_MODULE_OK;
185         }
186 #endif
187         return RLM_MODULE_NOOP;
188 }
189
190 static rlm_rcode_t CC_HINT(nonnull) mod_authorize(UNUSED void * instance, REQUEST *request)
191 {
192         VALUE_PAIR *vp;
193         int rv;
194
195         /* try to find the MS-SoH payload */
196         vp = pairfind(request->packet->vps, 55, VENDORPEC_MICROSOFT, TAG_ANY);
197         if (!vp) {
198                 RDEBUG("SoH radius VP not found");
199                 return RLM_MODULE_NOOP;
200         }
201
202         RDEBUG("SoH radius VP found");
203         /* decode it */
204         rv = soh_verify(request, vp->vp_octets, vp->vp_length);
205         if (rv < 0) {
206                 return RLM_MODULE_FAIL;
207         }
208
209         return RLM_MODULE_OK;
210 }
211
212 extern module_t rlm_soh;
213 module_t rlm_soh = {
214         RLM_MODULE_INIT,
215         "SoH",
216         RLM_TYPE_THREAD_SAFE,           /* type */
217         sizeof(rlm_soh_t),
218         module_config,
219         mod_instantiate,                /* instantiation */
220         NULL,                   /* detach */
221         {
222                 NULL,                   /* authenticate */
223                 mod_authorize,          /* authorize */
224                 NULL,                   /* pre-accounting */
225                 NULL,                   /* accounting */
226                 NULL,                   /* checksimul */
227                 NULL,                   /* pre-proxy */
228                 NULL,                   /* post-proxy */
229                 mod_post_auth           /* post-auth */
230         },
231 };