iPXE
oncrpc.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2013 Marin Hannache <ipxe@mareo.fr>.
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License as
00006  * published by the Free Software Foundation; either version 2 of the
00007  * License, or any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00017  * 02110-1301, USA.
00018  */
00019 
00020 #include <stdint.h>
00021 #include <stdlib.h>
00022 #include <stdio.h>
00023 #include <string.h>
00024 #include <assert.h>
00025 #include <errno.h>
00026 #include <byteswap.h>
00027 #include <ipxe/socket.h>
00028 #include <ipxe/tcpip.h>
00029 #include <ipxe/in.h>
00030 #include <ipxe/iobuf.h>
00031 #include <ipxe/dhcp.h>
00032 #include <ipxe/xfer.h>
00033 #include <ipxe/open.h>
00034 #include <ipxe/uri.h>
00035 #include <ipxe/features.h>
00036 #include <ipxe/oncrpc.h>
00037 #include <ipxe/oncrpc_iob.h>
00038 #include <ipxe/init.h>
00039 #include <ipxe/settings.h>
00040 #include <ipxe/version.h>
00041 
00042 /** @file
00043  *
00044  * SUN ONC RPC protocol
00045  *
00046  */
00047 
00048 /** Set most significant bit to 1. */
00049 #define SET_LAST_FRAME( x ) ( (x) | 1 << 31 )
00050 #define GET_FRAME_SIZE( x ) ( (x) & ~( 1 << 31 ) )
00051 
00052 #define ONCRPC_CALL     0
00053 #define ONCRPC_REPLY    1
00054 
00055 /** AUTH NONE authentication flavor */
00056 struct oncrpc_cred oncrpc_auth_none = {
00057         .flavor = ONCRPC_AUTH_NONE,
00058         .length = 0
00059 };
00060 
00061 const struct setting uid_setting __setting ( SETTING_AUTH, uid ) = {
00062         .name        = "uid",
00063         .description = "User ID",
00064         .tag         = DHCP_EB_UID,
00065         .type        = &setting_type_uint32
00066 };
00067 
00068 const struct setting gid_setting __setting ( SETTING_AUTH, gid ) = {
00069         .name        = "gid",
00070         .description = "Group ID",
00071         .tag         = DHCP_EB_GID,
00072         .type        = &setting_type_uint32
00073 };
00074 
00075 /**
00076  * Initialize an ONC RPC AUTH SYS credential structure
00077  *
00078  * @v auth_sys          The structure to initialize
00079  *
00080  * The hostname field is filled with the value of the hostname setting, if the
00081  * hostname setting is empty, PRODUCT_SHORT_NAME (usually "iPXE") is used
00082  * instead.
00083  */
00084 int oncrpc_init_cred_sys ( struct oncrpc_cred_sys *auth_sys ) {
00085         if ( ! auth_sys )
00086                 return -EINVAL;
00087 
00088         fetch_string_setting_copy ( NULL, &hostname_setting,
00089                                     &auth_sys->hostname );
00090         if ( ! auth_sys->hostname )
00091                 if ( ! ( auth_sys->hostname = strdup ( product_short_name ) ) )
00092                         return -ENOMEM;
00093 
00094         auth_sys->uid         = fetch_uintz_setting ( NULL, &uid_setting );
00095         auth_sys->gid         = fetch_uintz_setting ( NULL, &uid_setting );
00096         auth_sys->aux_gid_len = 0;
00097         auth_sys->stamp       = 0;
00098 
00099         auth_sys->credential.flavor = ONCRPC_AUTH_SYS;
00100         auth_sys->credential.length = 16 +
00101                                       oncrpc_strlen ( auth_sys->hostname );
00102 
00103         return 0;
00104 }
00105 
00106 /**
00107  * Prepare an ONC RPC session structure to be used by the ONC RPC layer
00108  *
00109  * @v session           ONC RPC session
00110  * @v credential        Credential structure pointer
00111  * @v verifier          Verifier structure pointer
00112  * @v prog_name         ONC RPC program number
00113  * @v prog_vers         ONC RPC program version number
00114  */
00115 void oncrpc_init_session ( struct oncrpc_session *session,
00116                            struct oncrpc_cred *credential,
00117                            struct oncrpc_cred *verifier, uint32_t prog_name,
00118                            uint32_t prog_vers ) {
00119         if ( ! session )
00120                 return;
00121 
00122         session->rpc_id     = rand();
00123         session->credential = credential;
00124         session->verifier   = verifier;
00125         session->prog_name  = prog_name;
00126         session->prog_vers  = prog_vers;
00127 }
00128 
00129 int oncrpc_call ( struct interface *intf, struct oncrpc_session *session,
00130                   uint32_t proc_name, const struct oncrpc_field fields[] ) {
00131         size_t           frame_size;
00132         struct io_buffer *io_buf;
00133 
00134         if ( ! session )
00135                 return -EINVAL;
00136 
00137         struct oncrpc_field header[] = {
00138                 ONCRPC_FIELD ( int32, 0 ),
00139                 ONCRPC_FIELD ( int32, ++session->rpc_id ),
00140                 ONCRPC_FIELD ( int32, ONCRPC_CALL ),
00141                 ONCRPC_FIELD ( int32, ONCRPC_VERS ),
00142                 ONCRPC_FIELD ( int32, session->prog_name ),
00143                 ONCRPC_FIELD ( int32, session->prog_vers ),
00144                 ONCRPC_FIELD ( int32, proc_name ),
00145                 ONCRPC_FIELD ( cred, session->credential ),
00146                 ONCRPC_FIELD ( cred, session->verifier ),
00147                 ONCRPC_FIELD_END,
00148         };
00149 
00150         frame_size  = oncrpc_compute_size ( header );
00151         frame_size += oncrpc_compute_size ( fields );
00152 
00153         io_buf = alloc_iob ( frame_size );
00154         if ( ! io_buf )
00155                 return -ENOBUFS;
00156 
00157         header[0].value.int32 = SET_LAST_FRAME ( frame_size -
00158                                                  sizeof ( uint32_t ) );
00159 
00160         oncrpc_iob_add_fields ( io_buf, header );
00161         oncrpc_iob_add_fields ( io_buf, fields );
00162 
00163         return xfer_deliver_iob ( intf, iob_disown ( io_buf ) );
00164 }
00165 
00166 size_t oncrpc_compute_size ( const struct oncrpc_field fields[] ) {
00167 
00168         size_t i;
00169         size_t size = 0;
00170 
00171         for ( i = 0; fields[i].type != oncrpc_none; i++ ) {
00172                 switch ( fields[i].type ) {
00173                 case oncrpc_int32:
00174                         size += sizeof ( uint32_t );
00175                         break;
00176 
00177                 case oncrpc_int64:
00178                         size += sizeof ( uint64_t );
00179                         break;
00180 
00181                 case oncrpc_str:
00182                         size += oncrpc_strlen ( fields[i].value.str );
00183                         break;
00184 
00185                 case oncrpc_array:
00186                         size += oncrpc_align ( fields[i].value.array.length );
00187                         size += sizeof ( uint32_t );
00188                         break;
00189 
00190                 case oncrpc_intarray:
00191                         size += sizeof ( uint32_t ) *
00192                                 fields[i].value.intarray.length;
00193                         size += sizeof ( uint32_t );
00194                         break;
00195 
00196                 case oncrpc_cred:
00197                         size += fields[i].value.cred->length;
00198                         size += 2 * sizeof ( uint32_t );
00199                         break;
00200 
00201                 default:
00202                         return size;
00203                 }
00204         }
00205 
00206         return size;
00207 }
00208 
00209 /**
00210  * Parse an I/O buffer to extract a ONC RPC REPLY
00211  * @v session           ONC RPC session
00212  * @v reply             Reply structure where data will be saved
00213  * @v io_buf            I/O buffer
00214  */
00215 int oncrpc_get_reply ( struct oncrpc_session *session __unused,
00216                        struct oncrpc_reply *reply, struct io_buffer *io_buf ) {
00217         if ( ! reply || ! io_buf )
00218                 return -EINVAL;
00219 
00220         reply->frame_size = GET_FRAME_SIZE ( oncrpc_iob_get_int ( io_buf ) );
00221         reply->rpc_id     = oncrpc_iob_get_int ( io_buf );
00222 
00223         /* iPXE has no support for handling ONC RPC call */
00224         if ( oncrpc_iob_get_int ( io_buf ) != ONCRPC_REPLY )
00225                 return -ENOSYS;
00226 
00227         reply->reply_state = oncrpc_iob_get_int ( io_buf );
00228 
00229         if ( reply->reply_state == 0 )
00230         {
00231                 /* verifier.flavor */
00232                 oncrpc_iob_get_int ( io_buf );
00233                 /* verifier.length */
00234                 iob_pull ( io_buf, oncrpc_iob_get_int ( io_buf ));
00235 
00236                 /* We don't use the verifier in iPXE, let it be an empty
00237                    verifier. */
00238                 reply->verifier = &oncrpc_auth_none;
00239         }
00240 
00241         reply->accept_state = oncrpc_iob_get_int ( io_buf );
00242         reply->data         = io_buf;
00243 
00244         return 0;
00245 }