~ubuntu-branches/ubuntu/maverick/ipmitool/maverick

« back to all changes in this revision

Viewing changes to src/plugins/lanplus/lanplus_crypt.c

  • Committer: Bazaar Package Importer
  • Author(s): Petter Reinholdtsen
  • Date: 2005-04-07 01:18:44 UTC
  • Revision ID: james.westby@ubuntu.com-20050407011844-a1b206z5iefiu5vi
Tags: upstream-1.8.1
ImportĀ upstreamĀ versionĀ 1.8.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (c) 2003 Sun Microsystems, Inc.  All Rights Reserved.
 
3
 * 
 
4
 * Redistribution and use in source and binary forms, with or without
 
5
 * modification, are permitted provided that the following conditions
 
6
 * are met:
 
7
 * 
 
8
 * Redistribution of source code must retain the above copyright
 
9
 * notice, this list of conditions and the following disclaimer.
 
10
 * 
 
11
 * Redistribution in binary form must reproduce the above copyright
 
12
 * notice, this list of conditions and the following disclaimer in the
 
13
 * documentation and/or other materials provided with the distribution.
 
14
 * 
 
15
 * Neither the name of Sun Microsystems, Inc. or the names of
 
16
 * contributors may be used to endorse or promote products derived
 
17
 * from this software without specific prior written permission.
 
18
 * 
 
19
 * This software is provided "AS IS," without a warranty of any kind.
 
20
 * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
 
21
 * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
 
22
 * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED.
 
23
 * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE
 
24
 * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
 
25
 * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.  IN NO EVENT WILL
 
26
 * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA,
 
27
 * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR
 
28
 * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF
 
29
 * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
 
30
 * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
 
31
 * 
 
32
 * You acknowledge that this software is not designed or intended for use
 
33
 * in the design, construction, operation or maintenance of any nuclear
 
34
 * facility.
 
35
 */
 
36
 
 
37
#include <assert.h>
 
38
#include <string.h>
 
39
#include <config.h>
 
40
#include <ipmitool/bswap.h>
 
41
#include <ipmitool/log.h>
 
42
#include "lanplus.h"
 
43
#include "lanplus_crypt.h"
 
44
#include "lanplus_crypt_impl.h"
 
45
 
 
46
 
 
47
 
 
48
/*
 
49
 * lanplus_rakp2_hmac_matches
 
50
 * 
 
51
 * param session holds all the state data that we need to generate the hmac
 
52
 * param hmac is the HMAC sent by the BMC in the RAKP 2 message
 
53
 *
 
54
 * The HMAC was generated [per RFC2404] from : 
 
55
 * 
 
56
 *     SIDm     - Remote console session ID    
 
57
 *     SIDc     - BMC session ID
 
58
 *     Rm       - Remote console random number
 
59
 *     Rc       - BMC random number
 
60
 *     GUIDc    - BMC guid
 
61
 *     ROLEm    - Requested privilege level (entire byte)
 
62
 *     ULENGTHm - Username length
 
63
 *     <UNAMEm> - Username (absent for null user names)
 
64
 *
 
65
 * generated by using Kuid.  I am aware that the subscripts on the values
 
66
 * look backwards, but that's the way they are written in the specification.
 
67
 *
 
68
 * If the authentication algorithm is IPMI_AUTH_RAKP_NONE, we return success.
 
69
 * 
 
70
 * return 0 on success (the authcode matches)
 
71
 *        1 on failure (the authcode does not match)
 
72
 */
 
73
int lanplus_rakp2_hmac_matches(const struct ipmi_session * session,
 
74
                                                           const uint8_t    * bmc_mac,
 
75
                                                           struct ipmi_intf * intf)
 
76
{
 
77
        char         * buffer;
 
78
        int           bufferLength, i;
 
79
        uint8_t       mac[20];
 
80
        uint32_t      macLength;
 
81
 
 
82
        uint32_t SIDm_lsbf, SIDc_lsbf;
 
83
 
 
84
 
 
85
        if (session->v2_data.auth_alg == IPMI_AUTH_RAKP_NONE)
 
86
                return 1;
 
87
        
 
88
        /* We don't yet support other algorithms */
 
89
        assert(session->v2_data.auth_alg == IPMI_AUTH_RAKP_HMAC_SHA1);
 
90
        
 
91
 
 
92
        bufferLength =
 
93
                4  +                       /* SIDm     */
 
94
                4  +                       /* SIDc     */
 
95
                16 +                       /* Rm       */
 
96
                16 +                       /* Rc       */
 
97
                16 +                       /* GUIDc    */
 
98
                1  +                       /* ROLEm    */
 
99
                1  +                       /* ULENGTHm */
 
100
                strlen(session->username); /* optional */
 
101
 
 
102
        buffer = malloc(bufferLength);
 
103
        if (buffer == NULL) {
 
104
                lprintf(LOG_ERR, "ipmitool: malloc failure");
 
105
                return 1;
 
106
        }
 
107
 
 
108
        /*
 
109
         * Fill the buffer.  I'm assuming that we're using the LSBF representation of the
 
110
         * multibyte numbers in use.
 
111
         */
 
112
 
 
113
        /* SIDm */
 
114
        SIDm_lsbf = session->v2_data.console_id;
 
115
        #if WORDS_BIGENDIAN
 
116
        SIDm_lsbf = BSWAP_32(SIDm_lsbf);
 
117
        #endif
 
118
 
 
119
        memcpy(buffer, &SIDm_lsbf, 4);
 
120
 
 
121
        /* SIDc */
 
122
        SIDc_lsbf = session->v2_data.bmc_id;
 
123
        #if WORDS_BIGENDIAN
 
124
        SIDc_lsbf = BSWAP_32(SIDc_lsbf);
 
125
        #endif
 
126
        memcpy(buffer + 4, &SIDc_lsbf, 4);
 
127
 
 
128
        /* Rm */
 
129
        #if WORDS_BIGENDIAN
 
130
        for (i = 0; i < 16; ++i)
 
131
                buffer[8 + i] = session->v2_data.console_rand[16 - 1 - i];
 
132
        #else
 
133
        for (i = 0; i < 16; ++i)
 
134
                buffer[8 + i] = session->v2_data.console_rand[i];
 
135
        #endif
 
136
 
 
137
        /* Rc */
 
138
        #if WORDS_BIGENDIAN
 
139
        for (i = 0; i < 16; ++i)
 
140
                buffer[24 + i] = session->v2_data.bmc_rand[16 - 1 - i];
 
141
        #else
 
142
        for (i = 0; i < 16; ++i)
 
143
                buffer[24 + i] = session->v2_data.bmc_rand[i];
 
144
        #endif
 
145
 
 
146
        /* GUIDc */
 
147
        #if WORDS_BIGENDIAN
 
148
        for (i = 0; i < 16; ++i)
 
149
                buffer[40 + i] = session->v2_data.bmc_guid[16 - 1 - i];
 
150
        #else
 
151
        for (i = 0; i < 16; ++i)
 
152
                buffer[40 + i] = session->v2_data.bmc_guid[i];
 
153
        #endif
 
154
 
 
155
        /* ROLEm */
 
156
        buffer[56] = session->v2_data.requested_role;
 
157
 
 
158
        /* ULENGTHm */
 
159
        buffer[57] = strlen(session->username);
 
160
 
 
161
        /* UserName [optional] */
 
162
        for (i = 0; i < buffer[57]; ++i)
 
163
                buffer[58 + i] = session->username[i];
 
164
 
 
165
        if (verbose > 2)
 
166
        {
 
167
                printbuf(buffer, bufferLength, ">> rakp2 mac input buffer");
 
168
                printbuf((char*)(session->authcode), IPMI_AUTHCODE_BUFFER_SIZE, ">> rakp2 mac key");
 
169
        }
 
170
 
 
171
        /*
 
172
         * The buffer is complete.  Let's hash.
 
173
         */
 
174
        lanplus_HMAC(session->v2_data.auth_alg,
 
175
                                 session->authcode,
 
176
                                 IPMI_AUTHCODE_BUFFER_SIZE,
 
177
                                 buffer,
 
178
                                 bufferLength,
 
179
                                 mac,
 
180
                                 &macLength);
 
181
 
 
182
        free(buffer);
 
183
 
 
184
 
 
185
        if (verbose > 2)
 
186
        {
 
187
                printbuf(mac, macLength, ">> rakp2 mac as computed by the remote console");
 
188
        }
 
189
 
 
190
        return (memcmp(bmc_mac, mac, macLength) == 0);
 
191
}
 
192
 
 
193
 
 
194
 
 
195
/*
 
196
 * lanplus_rakp4_hmac_matches
 
197
 * 
 
198
 * param session holds all the state data that we need to generate the hmac
 
199
 * param hmac is the HMAC sent by the BMC in the RAKP 4 message
 
200
 *
 
201
 * The HMAC was generated [per RFC2404] from : 
 
202
 * 
 
203
 *     Rm       - Remote console random number
 
204
 *     SIDc     - BMC session ID
 
205
 *     GUIDc    - BMC guid
 
206
 *
 
207
 * generated by using SIK (the session integrity key).  I am aware that the
 
208
 * subscripts on the values look backwards, but that's the way they are
 
209
 * written in the specification.
 
210
 *
 
211
 * If the authentication algorithm is IPMI_AUTH_RAKP_NONE, we return success.
 
212
 * 
 
213
 * return 1 on success (the authcode matches)
 
214
 *        0 on failure (the authcode does not match)
 
215
 *
 
216
 */
 
217
int lanplus_rakp4_hmac_matches(const struct ipmi_session * session,
 
218
                                                           const uint8_t    * bmc_mac,
 
219
                                                           struct ipmi_intf * intf)
 
220
{
 
221
        char         * buffer;
 
222
        int           bufferLength, i;
 
223
        uint8_t       mac[20];
 
224
        uint32_t      macLength;
 
225
 
 
226
        uint32_t SIDc_lsbf;
 
227
 
 
228
        if (ipmi_oem_active(intf, "intelplus")){
 
229
                /* Intel BMC responds with the integrity Algorithm in RAKP4 */
 
230
                if (session->v2_data.integrity_alg == IPMI_INTEGRITY_NONE)
 
231
                        return 1;
 
232
                
 
233
                /* We don't yet support other algorithms */
 
234
                assert(session->v2_data.integrity_alg == IPMI_INTEGRITY_HMAC_SHA1_96);
 
235
                
 
236
        } else {
 
237
                if (session->v2_data.auth_alg == IPMI_AUTH_RAKP_NONE)
 
238
                        return 1;               
 
239
 
 
240
                /* We don't yet support other algorithms */     
 
241
                assert(session->v2_data.auth_alg == IPMI_AUTH_RAKP_HMAC_SHA1);  
 
242
        }
 
243
 
 
244
        bufferLength =
 
245
                16 +   /* Rm    */
 
246
                4  +   /* SIDc  */
 
247
                16;    /* GUIDc */
 
248
 
 
249
        buffer = (char*)malloc(bufferLength);
 
250
        if (buffer == NULL) {
 
251
                lprintf(LOG_ERR, "ipmitool: malloc failure");
 
252
                return 1;
 
253
        }
 
254
 
 
255
        /*
 
256
         * Fill the buffer.  I'm assuming that we're using the LSBF representation of the
 
257
         * multibyte numbers in use.
 
258
         */
 
259
 
 
260
        /* Rm */
 
261
        #if WORDS_BIGENDIAN
 
262
        for (i = 0; i < 16; ++i)
 
263
                buffer[i] = session->v2_data.console_rand[16 - 1 - i];
 
264
        #else
 
265
        for (i = 0; i < 16; ++i)
 
266
                buffer[i] = session->v2_data.console_rand[i];
 
267
        #endif
 
268
 
 
269
 
 
270
        /* SIDc */
 
271
        SIDc_lsbf = session->v2_data.bmc_id;
 
272
        #if WORDS_BIGENDIAN
 
273
        SIDc_lsbf = BSWAP_32(SIDc_lsbf);
 
274
        #endif
 
275
        memcpy(buffer + 16, &SIDc_lsbf, 4);
 
276
        
 
277
 
 
278
        /* GUIDc */
 
279
        #if WORDS_BIGENDIAN
 
280
        for (i = 0; i < 16; ++i)
 
281
                buffer[i +  20] = session->v2_data.bmc_guid[16 - 1 - i];
 
282
        #else
 
283
        for (i = 0; i < 16; ++i)
 
284
                buffer[i +  20] = session->v2_data.bmc_guid[i];
 
285
        #endif
 
286
 
 
287
 
 
288
    if (verbose > 2)
 
289
    {
 
290
        printbuf(buffer, bufferLength, ">> rakp4 mac input buffer");
 
291
        printbuf(session->v2_data.sik, 20l, ">> rakp4 mac key (sik)");
 
292
    }
 
293
 
 
294
 
 
295
        /*
 
296
         * The buffer is complete.  Let's hash.
 
297
         */
 
298
        lanplus_HMAC((ipmi_oem_active(intf, "intelplus")) 
 
299
                     ? session->v2_data.integrity_alg 
 
300
                     : session->v2_data.auth_alg ,
 
301
                                 session->v2_data.sik,
 
302
                                 IPMI_SIK_BUFFER_SIZE,
 
303
                                 buffer,
 
304
                                 bufferLength,
 
305
                                 mac,
 
306
                                 &macLength);
 
307
 
 
308
    if (verbose > 2)
 
309
        {
 
310
                printbuf(bmc_mac, macLength, ">> rakp4 mac as computed by the BMC");
 
311
                printbuf(mac,     macLength, ">> rakp4 mac as computed by the remote console");
 
312
        }
 
313
 
 
314
 
 
315
 
 
316
        free(buffer);
 
317
        assert(macLength == 20);
 
318
        return (memcmp(bmc_mac, mac, 12) == 0);
 
319
}
 
320
 
 
321
 
 
322
 
 
323
/*
 
324
 * lanplus_generate_rakp3_auth_code
 
325
 *
 
326
 * This auth code is an HMAC generated with :
 
327
 *
 
328
 *     Rc         - BMC random number
 
329
 *     SIDm       - Console session ID
 
330
 *     ROLEm      - Requested privilege level (entire byte)
 
331
 *     ULENGTHm   - Username length
 
332
 *     <USERNAME> - Usename (absent for null usernames)
 
333
 *
 
334
 * The key used to generated the MAC is Kuid
 
335
 *
 
336
 * I am aware that the subscripts look backwards, but that is the way they are
 
337
 * written in the spec.
 
338
 * 
 
339
 * param output_buffer [out] will hold the generated MAC
 
340
 * param session       [in]  holds all the state data we need to generate the auth code
 
341
 * param mac_length    [out] will be set to the length of the auth code
 
342
 *
 
343
 * returns 0 on success
 
344
 *         1 on failure
 
345
 */
 
346
int lanplus_generate_rakp3_authcode(char                      * output_buffer,
 
347
                                                                        const struct ipmi_session * session,
 
348
                                                                        uint32_t                  * mac_length,
 
349
                                                                        struct ipmi_intf          * intf)
 
350
{
 
351
        int ret = 0;
 
352
        int input_buffer_length, i;
 
353
        char * input_buffer;
 
354
        uint32_t SIDm_lsbf;
 
355
        
 
356
 
 
357
        if (session->v2_data.auth_alg == IPMI_AUTH_RAKP_NONE)
 
358
        {
 
359
                *mac_length = 0;
 
360
                return 0;
 
361
        }
 
362
 
 
363
        /* We don't yet support other algorithms */
 
364
        assert(session->v2_data.auth_alg == IPMI_AUTH_RAKP_HMAC_SHA1);
 
365
 
 
366
        input_buffer_length =
 
367
                16 + /* Rc       */
 
368
                4  + /* SIDm     */
 
369
                1  + /* ROLEm    */
 
370
                1  + /* ULENGTHm */
 
371
                strlen(session->username);
 
372
 
 
373
        input_buffer = malloc(input_buffer_length);
 
374
        if (input_buffer == NULL) {
 
375
                lprintf(LOG_ERR, "ipmitool: malloc failure");
 
376
                return 1;
 
377
        }
 
378
 
 
379
        /*
 
380
         * Fill the buffer.  I'm assuming that we're using the LSBF representation of the
 
381
         * multibyte numbers in use.
 
382
         */
 
383
        
 
384
        /* Rc */
 
385
        #if WORDS_BIGENDIAN
 
386
        for (i = 0; i < 16; ++i)
 
387
                input_buffer[i] = session->v2_data.bmc_rand[16 - 1 - i];
 
388
        #else
 
389
        for (i = 0; i < 16; ++i)
 
390
                input_buffer[i] = session->v2_data.bmc_rand[i];
 
391
        #endif
 
392
 
 
393
        /* SIDm */
 
394
        SIDm_lsbf = session->v2_data.console_id;
 
395
        #if WORDS_BIGENDIAN
 
396
        SIDm_lsbf = BSWAP_32(SIDm_lsbf);
 
397
        #endif
 
398
        memcpy(input_buffer + 16, &SIDm_lsbf, 4);
 
399
        
 
400
        /* ROLEm */
 
401
        if (ipmi_oem_active(intf, "intelplus")) 
 
402
                input_buffer[20] = session->privlvl;
 
403
        else 
 
404
                input_buffer[20] = session->v2_data.requested_role;
 
405
 
 
406
        /* ULENGTHm */
 
407
        input_buffer[21] = strlen(session->username);
 
408
 
 
409
        /* USERNAME */
 
410
        for (i = 0; i < input_buffer[21]; ++i)
 
411
                input_buffer[22 + i] = session->username[i];
 
412
 
 
413
    if (verbose > 2)
 
414
    {
 
415
        printbuf(input_buffer, input_buffer_length, ">> rakp3 mac input buffer");
 
416
        printbuf((char*)(session->authcode), IPMI_AUTHCODE_BUFFER_SIZE, ">> rakp3 mac key");
 
417
    }
 
418
    
 
419
        lanplus_HMAC(session->v2_data.auth_alg,
 
420
                                 session->authcode,
 
421
                                 IPMI_AUTHCODE_BUFFER_SIZE,
 
422
                                 input_buffer,
 
423
                                 input_buffer_length,
 
424
                                 output_buffer,
 
425
                                 mac_length);
 
426
 
 
427
    if (verbose > 2)
 
428
        printbuf(output_buffer, *mac_length, "generated rakp3 mac");
 
429
 
 
430
        
 
431
        free(input_buffer);
 
432
 
 
433
        return ret;
 
434
}
 
435
 
 
436
 
 
437
 
 
438
/*
 
439
 * lanplus_generate_sik
 
440
 *
 
441
 * Generate the session integrity key (SIK) used for integrity checking 
 
442
 * during the IPMI v2 / RMCP+ session
 
443
 *
 
444
 * This session integrity key is a HMAC generated with :
 
445
 *
 
446
 *     Rm         - Console generated random number
 
447
 *     Rc         - BMC generated random number
 
448
 *     ROLEm      - Requested privilege level (entire byte)
 
449
 *     ULENGTHm   - Username length
 
450
 *     <USERNAME> - Usename (absent for null usernames)
 
451
 *
 
452
 * The key used to generated the SIK is Kg if Kg is not null (two-key logins are
 
453
 * enabled).  Otherwise Kuid (the user authcode) is used as the key to genereate
 
454
 * the SIK.
 
455
 *
 
456
 * I am aware that the subscripts look backwards, but that is the way they are
 
457
 * written in the spec.
 
458
 * 
 
459
 * param session [in/out] contains our input and output fields.
 
460
 *
 
461
 * returns 0 on success
 
462
 *         1 on failure
 
463
 */
 
464
int lanplus_generate_sik(struct ipmi_session * session)
 
465
{
 
466
        char * input_buffer;
 
467
        int input_buffer_length, i;
 
468
        char * input_key;
 
469
        uint32_t mac_length;
 
470
        
 
471
 
 
472
        memset(session->v2_data.sik, 0, IPMI_SIK_BUFFER_SIZE);
 
473
 
 
474
        if (session->v2_data.auth_alg == IPMI_AUTH_RAKP_NONE)
 
475
                return 0;
 
476
 
 
477
        /* We don't yet support other algorithms */
 
478
        assert(session->v2_data.auth_alg == IPMI_AUTH_RAKP_HMAC_SHA1);
 
479
 
 
480
        input_buffer_length =
 
481
                16 +  /* Rm       */
 
482
                16 +  /* Rc       */
 
483
                1  +  /* ROLEm     */
 
484
                1  +  /* ULENGTHm  */
 
485
                strlen(session->username);
 
486
 
 
487
        input_buffer = malloc(input_buffer_length);
 
488
        if (input_buffer == NULL) {
 
489
                lprintf(LOG_ERR, "ipmitool: malloc failure");
 
490
                return 1;
 
491
        }
 
492
 
 
493
        /*
 
494
         * Fill the buffer.  I'm assuming that we're using the LSBF representation of the
 
495
         * multibyte numbers in use.
 
496
         */
 
497
 
 
498
        /* Rm */
 
499
        #if WORDS_BIGENDIAN
 
500
        for (i = 0; i < 16; ++i)
 
501
                input_buffer[i] = session->v2_data.console_rand[16 - 1 - i];
 
502
        #else
 
503
        for (i = 0; i < 16; ++i)
 
504
                input_buffer[i] = session->v2_data.console_rand[i];
 
505
        #endif
 
506
        
 
507
 
 
508
        /* Rc */
 
509
        #if WORDS_BIGENDIAN
 
510
        for (i = 0; i < 16; ++i)
 
511
                input_buffer[16 + i] = session->v2_data.bmc_rand[16 - 1 - i];
 
512
        #else
 
513
        for (i = 0; i < 16; ++i)
 
514
                input_buffer[16 + i] = session->v2_data.bmc_rand[i];
 
515
        #endif
 
516
 
 
517
        /* ROLEm */
 
518
        input_buffer[32] = session->v2_data.requested_role;
 
519
 
 
520
        /* ULENGTHm */
 
521
        input_buffer[33] = strlen(session->username);
 
522
 
 
523
        /* USERNAME */
 
524
        for (i = 0; i < input_buffer[33]; ++i)
 
525
                input_buffer[34 + i] = session->username[i];
 
526
 
 
527
        if (session->v2_data.kg[0])
 
528
        {
 
529
                /* We will be hashing with Kg */
 
530
                /*
 
531
                 * TODO: Section 13.31 of the IPMI v2 spec describes the SIK creation
 
532
                 * using Kg.  It specifies that Kg should not be truncated, but I
 
533
                 * do not know what is meant by that.
 
534
                 */
 
535
                lprintf(LOG_ERR, "lanplus_generate_sik: We dont yet support hashing with Kg");
 
536
                assert(0);
 
537
 
 
538
                input_key        = session->v2_data.kg;
 
539
        }
 
540
        else
 
541
        {
 
542
                /* We will be hashing with Kuid */
 
543
                input_key        = session->authcode;
 
544
        }
 
545
 
 
546
        
 
547
        if (verbose >= 2)
 
548
                printbuf(input_buffer, input_buffer_length, "session integrity key input");
 
549
 
 
550
        lanplus_HMAC(session->v2_data.auth_alg,
 
551
                                 input_key,
 
552
                                 IPMI_AUTHCODE_BUFFER_SIZE,
 
553
                                 input_buffer,
 
554
                                 input_buffer_length,
 
555
                                 session->v2_data.sik,
 
556
                                 &mac_length);
 
557
 
 
558
        free(input_buffer);
 
559
        assert(mac_length == 20);
 
560
 
 
561
        /*
 
562
         * The key MAC generated is 20 bytes, but we will only be using the first
 
563
         * 12 for SHA1 96
 
564
         */
 
565
        if (verbose >= 2)
 
566
                printbuf(session->v2_data.sik, 20, "Generated session integrity key");
 
567
 
 
568
        return 0;
 
569
}
 
570
 
 
571
 
 
572
 
 
573
/*
 
574
 * lanplus_generate_k1
 
575
 *
 
576
 * Generate K1, the key presumably used to generate integrity authcodes
 
577
 *
 
578
 * We use the authentication algorithm to generated the HMAC, using
 
579
 * the session integrity key (SIK) as our key.
 
580
 *
 
581
 * param session [in/out].
 
582
 *
 
583
 * returns 0 on success
 
584
 *         1 on failure
 
585
 */
 
586
int lanplus_generate_k1(struct ipmi_session * session)
 
587
{
 
588
        uint32_t mac_length;
 
589
 
 
590
        uint8_t CONST_1[] =
 
591
                {0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
 
592
                 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
 
593
 
 
594
        if (session->v2_data.auth_alg == IPMI_AUTH_RAKP_NONE)
 
595
                memcpy(session->v2_data.k1, CONST_1, 20);
 
596
        else
 
597
        {
 
598
                lanplus_HMAC(session->v2_data.auth_alg,
 
599
                                         session->v2_data.sik,
 
600
                                         IPMI_SIK_BUFFER_SIZE, /* SIK length */
 
601
                                         CONST_1,
 
602
                                         20,
 
603
                                         session->v2_data.k1,
 
604
                                         &mac_length);
 
605
                assert(mac_length == 20);
 
606
        }
 
607
 
 
608
        if (verbose >= 2)
 
609
                printbuf(session->v2_data.k1, 20, "Generated K1");
 
610
 
 
611
        return 0;
 
612
}
 
613
 
 
614
 
 
615
 
 
616
/*
 
617
 * lanplus_generate_k2
 
618
 *
 
619
 * Generate K2, the key used for RMCP+ AES encryption.
 
620
 *
 
621
 * We use the authentication algorithm to generated the HMAC, using
 
622
 * the session integrity key (SIK) as our key.
 
623
 *
 
624
 * param session [in/out].
 
625
 *
 
626
 * returns 0 on success
 
627
 *         1 on failure
 
628
 */
 
629
int lanplus_generate_k2(struct ipmi_session * session)
 
630
{
 
631
        uint32_t mac_length;
 
632
 
 
633
        uint8_t CONST_2[] =
 
634
                {0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
 
635
                 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02};
 
636
 
 
637
        if (session->v2_data.auth_alg == IPMI_AUTH_RAKP_NONE)
 
638
                memcpy(session->v2_data.k2, CONST_2, 20);
 
639
        else
 
640
        {
 
641
                lanplus_HMAC(session->v2_data.auth_alg,
 
642
                                         session->v2_data.sik,
 
643
                                         IPMI_SIK_BUFFER_SIZE, /* SIK length */
 
644
                                         CONST_2,
 
645
                                         20,
 
646
                                         session->v2_data.k2,
 
647
                                         &mac_length);
 
648
                assert(mac_length == 20);
 
649
        }
 
650
 
 
651
        if (verbose >= 2)
 
652
                printbuf(session->v2_data.k2, 20, "Generated K2");
 
653
 
 
654
        return 0;
 
655
}
 
656
 
 
657
 
 
658
 
 
659
/*
 
660
 * lanplus_encrypt_payload
 
661
 *
 
662
 * Perform the appropriate encryption on the input data.  Output the encrypted
 
663
 * data to output, including the required confidentiality header and trailer.
 
664
 * If the crypt_alg is IPMI_CRYPT_NONE, simply copy the input to the output and
 
665
 * set bytes_written to input_length.
 
666
 * 
 
667
 * param crypt_alg specifies the encryption algorithm (from table 13-19 of the
 
668
 *       IPMI v2 spec)
 
669
 * param key is the used as input to the encryption algorithmf
 
670
 * param input is the input data to be encrypted
 
671
 * param input_length is the length of the input data to be encrypted
 
672
 * param output is the cipher text generated by the encryption process
 
673
 * param bytes_written is the number of bytes written during the encryption
 
674
 *       process
 
675
 *
 
676
 * returns 0 on success
 
677
 *         1 on failure
 
678
 */
 
679
int lanplus_encrypt_payload(uint8_t         crypt_alg,
 
680
                                                        const uint8_t * key,
 
681
                                                        const uint8_t * input,
 
682
                                                        uint32_t          input_length,
 
683
                                                        uint8_t       * output,
 
684
                                                        uint16_t      * bytes_written)
 
685
{
 
686
        uint8_t * padded_input;
 
687
        uint32_t    mod, i, bytes_encrypted;
 
688
        uint8_t   pad_length = 0;
 
689
 
 
690
        if (crypt_alg == IPMI_CRYPT_NONE)
 
691
        {
 
692
                /* Just copy the input to the output */
 
693
                *bytes_written = input_length;
 
694
                return 0;
 
695
        }
 
696
        
 
697
        /* Currently, we only support AES */
 
698
        assert(crypt_alg == IPMI_CRYPT_AES_CBC_128);
 
699
        assert(input_length <= IPMI_MAX_PAYLOAD_SIZE);
 
700
 
 
701
 
 
702
        /*
 
703
         * The input to the AES encryption algorithm has to be a multiple of the
 
704
         * block size (16 bytes).  The extra byte we are adding is the pad length
 
705
         * byte.
 
706
         */
 
707
        mod = (input_length + 1) % IPMI_CRYPT_AES_CBC_128_BLOCK_SIZE;
 
708
        if (mod)
 
709
                pad_length = IPMI_CRYPT_AES_CBC_128_BLOCK_SIZE - mod;
 
710
 
 
711
        padded_input = (uint8_t*)malloc(input_length + pad_length + 1);
 
712
        if (padded_input == NULL) {
 
713
                lprintf(LOG_ERR, "ipmitool: malloc failure");
 
714
                return 1;
 
715
        }
 
716
        memcpy(padded_input, input, input_length);
 
717
 
 
718
        /* add the pad */
 
719
        for (i = 0; i < pad_length; ++i)
 
720
                padded_input[input_length + i] = i + 1;
 
721
 
 
722
        /* add the pad length */
 
723
        padded_input[input_length + pad_length] = pad_length;
 
724
 
 
725
        /* Generate an initialization vector, IV, for the encryption process */
 
726
        if (lanplus_rand(output, IPMI_CRYPT_AES_CBC_128_BLOCK_SIZE))
 
727
        {
 
728
                lprintf(LOG_ERR, "lanplus_encrypt_payload: Error generating IV");
 
729
                return 1;
 
730
        }
 
731
 
 
732
        if (verbose > 2)
 
733
                printbuf(output, IPMI_CRYPT_AES_CBC_128_BLOCK_SIZE, ">> Initialization vector");
 
734
 
 
735
 
 
736
 
 
737
        lanplus_encrypt_aes_cbc_128(output,                                     /* IV              */
 
738
                                                                key,                                        /* K2              */
 
739
                                                                padded_input,                               /* Data to encrypt */
 
740
                                                                input_length + pad_length + 1,              /* Input length    */
 
741
                                                                output + IPMI_CRYPT_AES_CBC_128_BLOCK_SIZE, /* output          */
 
742
                                                                &bytes_encrypted);                          /* bytes written   */
 
743
 
 
744
        *bytes_written =
 
745
                IPMI_CRYPT_AES_CBC_128_BLOCK_SIZE + /* IV */
 
746
                bytes_encrypted;
 
747
 
 
748
        free(padded_input);
 
749
 
 
750
        return 0;
 
751
}
 
752
 
 
753
 
 
754
 
 
755
/*
 
756
 * lanplus_has_valid_auth_code
 
757
 *
 
758
 * Determine whether the packets authcode field is valid for packet.
 
759
 * 
 
760
 * We always return success if any of the following are true. 
 
761
 *  - this is not an IPMIv2 packet
 
762
 *  - the session is not yet active
 
763
 *  - the packet specifies that it is not authenticated
 
764
 *  - the integrity algorithm agreed upon during session creation is "none"
 
765
 *
 
766
 * The authcode is computed using the specified integrity algorithm starting
 
767
 * with the AuthType / Format field, and ending with the field immediately
 
768
 * preceeding the authcode itself.
 
769
 *
 
770
 * The key key used to generate the authcode MAC is K1.
 
771
 * 
 
772
 * param rs holds the response structure.
 
773
 * param session holds our session state, including our chosen algorithm, key, etc.
 
774
 *
 
775
 * returns 1 on success (authcode is valid)
 
776
 *         0 on failure (autchode integrity check failed)
 
777
 */
 
778
int lanplus_has_valid_auth_code(struct ipmi_rs * rs,
 
779
                                                                struct ipmi_session * session)
 
780
{
 
781
        uint8_t * bmc_authcode;
 
782
        uint8_t   generated_authcode[IPMI_MAX_MAC_SIZE];
 
783
        uint32_t    generated_authcode_length;
 
784
        
 
785
 
 
786
        if ((rs->session.authtype != IPMI_SESSION_AUTHTYPE_RMCP_PLUS) ||
 
787
                (session->v2_data.session_state != LANPLUS_STATE_ACTIVE)  ||
 
788
                (! rs->session.bAuthenticated)                            ||
 
789
                (session->v2_data.integrity_alg == IPMI_INTEGRITY_NONE))
 
790
                return 1;
 
791
        
 
792
        /* We only support SHA1-96 now */
 
793
        assert(session->v2_data.integrity_alg == IPMI_INTEGRITY_HMAC_SHA1_96);
 
794
 
 
795
        /*
 
796
         * For SHA1-96, the authcode will be the last 12 bytes in the packet
 
797
         */
 
798
        bmc_authcode = rs->data + (rs->data_len - IPMI_SHA1_AUTHCODE_SIZE);
 
799
 
 
800
        lanplus_HMAC(session->v2_data.integrity_alg,
 
801
                                 session->v2_data.k1,
 
802
                                 IPMI_AUTHCODE_BUFFER_SIZE,
 
803
                                 rs->data + IMPI_LANPLUS_OFFSET_AUTHTYPE,
 
804
                                 rs->data_len - IMPI_LANPLUS_OFFSET_AUTHTYPE - IPMI_SHA1_AUTHCODE_SIZE,
 
805
                                 generated_authcode,
 
806
                                 &generated_authcode_length);
 
807
 
 
808
        if (verbose > 3)
 
809
        {
 
810
                lprintf(LOG_DEBUG+2, "Validating authcode");
 
811
                printbuf(session->v2_data.k1, 20, "K1");
 
812
                printbuf(rs->data + IMPI_LANPLUS_OFFSET_AUTHTYPE,
 
813
                                 rs->data_len - IMPI_LANPLUS_OFFSET_AUTHTYPE - IPMI_SHA1_AUTHCODE_SIZE,
 
814
                                 "Authcode Input Data");
 
815
                printbuf(generated_authcode, 12, "Generated authcode");
 
816
                printbuf(bmc_authcode,       12, "Expected authcode");
 
817
        }
 
818
 
 
819
        
 
820
        assert(generated_authcode_length == 20);
 
821
        return (memcmp(bmc_authcode, generated_authcode, 12) == 0);
 
822
}
 
823
 
 
824
 
 
825
 
 
826
/*
 
827
 * lanplus_decrypt_payload
 
828
 *
 
829
 * 
 
830
 * param input points to the beginning of the payload (which will be the IV if
 
831
 *       we are using AES)
 
832
 * param payload_size [out] will be set to the size of the payload EXCLUDING
 
833
 * padding
 
834
 * 
 
835
 * returns 0 on success (we were able to successfully decrypt the packet)
 
836
 *         1 on failure (we were unable to successfully decrypt the packet)
 
837
 */
 
838
int lanplus_decrypt_payload(uint8_t         crypt_alg,
 
839
                                                        const uint8_t * key,
 
840
                                                        const uint8_t * input,
 
841
                                                        uint32_t          input_length,
 
842
                                                        uint8_t       * output,
 
843
                                                        uint16_t      * payload_size)
 
844
{
 
845
        uint8_t * decrypted_payload;
 
846
        uint32_t    bytes_decrypted;
 
847
 
 
848
        if (crypt_alg == IPMI_CRYPT_NONE)
 
849
        {
 
850
                /* We are not encrypted.  The paylaod size is is everything. */
 
851
                *payload_size = input_length;
 
852
                memmove(output, input, input_length);
 
853
                return 0;
 
854
        }
 
855
 
 
856
        /* We only support AES */
 
857
        assert(crypt_alg == IPMI_CRYPT_AES_CBC_128);
 
858
 
 
859
        decrypted_payload = (uint8_t*)malloc(input_length);
 
860
        if (decrypted_payload == NULL) {
 
861
                lprintf(LOG_ERR, "ipmitool: malloc failure");
 
862
                return 1;
 
863
        }
 
864
 
 
865
 
 
866
        lanplus_decrypt_aes_cbc_128(input,                                /* IV              */
 
867
                                                                key,                                  /* Key             */
 
868
                                                                input                        +
 
869
                                                                IPMI_CRYPT_AES_CBC_128_BLOCK_SIZE,    /* Data to decrypt */
 
870
                                                                input_length -
 
871
                                                                IPMI_CRYPT_AES_CBC_128_BLOCK_SIZE,    /* Input length    */
 
872
                                                                decrypted_payload,                    /* output          */
 
873
                                                                &bytes_decrypted);                    /* bytes written   */
 
874
 
 
875
        if (bytes_decrypted != 0)
 
876
        {
 
877
                /* Success */
 
878
                uint8_t conf_pad_length;
 
879
                int i;
 
880
 
 
881
                memmove(output,
 
882
                                decrypted_payload,
 
883
                                bytes_decrypted);
 
884
 
 
885
                /*
 
886
                 * We have to determine the payload size, by substracting the padding, etc.
 
887
                 * The last byte of the decrypted payload is the confidentiality pad length.
 
888
                 */
 
889
                conf_pad_length = decrypted_payload[bytes_decrypted - 1];
 
890
                *payload_size = bytes_decrypted - conf_pad_length - 1;
 
891
 
 
892
                /*
 
893
                 * Extra test to make sure that the padding looks like it should (should start
 
894
                 * with 0x01, 0x02, 0x03, etc...
 
895
                 */
 
896
                for (i = 0; i < conf_pad_length; ++i)
 
897
                {
 
898
                        if (decrypted_payload[*payload_size + i] == i)
 
899
                        {
 
900
                                lprintf(LOG_ERR, "Malformed payload padding");
 
901
                                assert(0);
 
902
                        }
 
903
                }
 
904
        }
 
905
        else
 
906
        {
 
907
                lprintf(LOG_ERR, "ERROR: lanplus_decrypt_aes_cbc_128 decryptd 0 bytes");
 
908
                assert(0);
 
909
        }
 
910
 
 
911
        free(decrypted_payload);
 
912
        return (bytes_decrypted == 0);
 
913
}
 
914