irods  4.2.8
About: iRODS (the integrated Rule Oriented Data System) is a distributed data-management system for creating data grids, digital libraries, persistent archives, and real-time data systems.
  Fossies Dox: irods-4.2.8.tar.gz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

osauth.cpp
Go to the documentation of this file.
1 
3 /*
4  These routines are used to implement the OS level authentication scheme.
5 
6 
7  Returns codes of 0 indicate success, others are iRODS error codes.
8 */
9 
10 #include <cstdlib>
11 #include <stdio.h>
12 #include <limits>
13 #include <string>
14 #include <vector>
15 #include <openssl/md5.h>
16 #include "boost/filesystem.hpp"
17 #include "irods_default_paths.hpp"
18 #ifndef windows_platform
19 #include <unistd.h>
20 #include <sys/types.h>
21 #include <sys/wait.h>
22 #include <sys/stat.h>
23 #include <spawn.h>
24 #endif
25 
26 #include "rods.h"
27 #include "osauth.h"
28 #include "rcGlobalExtern.h"
29 #include "authenticate.h"
30 
31 extern char **environ;
32 
33 extern "C" {
34 
35  int
36  osauthVerifyResponse( char *challenge, char *username, char *response ) {
37  static char fname[] = "osauthVerifyResponse";
38  char authenticator[16]; /* MD5 hash */
39  char md5buffer[CHALLENGE_LEN + MAX_PASSWORD_LEN + 2];
40  char md5digest[RESPONSE_LEN + 2];
41  int uid, status, i;
42  char *keybuf = NULL;
43  int key_len;
44  MD5_CTX ctx;
45 
46  uid = osauthGetUid( username );
47  if ( uid == -1 ) {
48  return SYS_USER_RETRIEVE_ERR;
49  }
50 
51  /* read the key from the key file */
52  if ( ( status = osauthGetKey( &keybuf, &key_len ) ) ) {
54  "%s: error retrieving key.", fname );
55  return status;
56  }
57 
58  /* generate the authenticator */
59  status = osauthGenerateAuthenticator( username, uid, challenge,
60  keybuf, key_len,
61  authenticator, 16 );
62  if ( status ) {
64  "%s: could not generate the authenticator", fname );
65  free( keybuf );
66  return status;
67  }
68  free( keybuf );
69 
70  /* now append this hash with the challenge, and
71  take the md5 sum of that */
72  memset( md5buffer, 0, sizeof( md5buffer ) );
73  memset( md5digest, 0, sizeof( md5digest ) );
74  memcpy( md5buffer, challenge, CHALLENGE_LEN );
75  memcpy( md5buffer + CHALLENGE_LEN, authenticator, 16 );
76  MD5_Init( &ctx );
77  MD5_Update( &ctx, ( unsigned char* )md5buffer, CHALLENGE_LEN + MAX_PASSWORD_LEN );
78  MD5_Final( ( unsigned char* )md5digest, &ctx );
79  for ( i = 0; i < RESPONSE_LEN; i++ ) {
80  /* make sure 'string' doesn't end early
81  (this matches client digest generation). */
82  if ( md5digest[i] == '\0' ) {
83  md5digest[i]++;
84  }
85  }
86 
87  /* compare the calculated digest to the client response */
88  for ( i = 0; i < RESPONSE_LEN; i++ ) {
89  if ( response[i] != md5digest[i] ) {
90  rodsLog( LOG_ERROR, "%s: calculated digest doesn't match client response",
91  fname );
93  }
94  }
95 
96  return 0;
97  }
98 
99  /*
100  * osauthGenerateAuthenticator - this functions creates
101  * an authenticator from the passed in parameters. The
102  * parameters are concatenated together, and then an
103  * md5 hash is generated and provided in the output
104  * parameter. Returns 0 on success, error code on error.
105  */
106  int
107  osauthGenerateAuthenticator( char *username, int uid,
108  char *challenge, char *key, int key_len,
109  char *authenticator, int authenticator_len ) {
110  static char fname[] = "osauthGenerateAuthenticator";
111  char *buffer, *bufp;
112  int buflen;
113  char md5digest[16];
114  MD5_CTX ctx;
115 
116  if ( authenticator == NULL ||
117  authenticator_len < 16 ) {
118  return USER_INPUT_OPTION_ERR;
119  }
120 
121  /* calculate buffer size and allocate */
122  buflen = username ? strlen( username ) : 0;
123  buflen += sizeof( uid ) + CHALLENGE_LEN + key_len;
124  buffer = ( char* )malloc( buflen );
125  if ( buffer == NULL ) {
127  "%s: could not allocate memory buffer. errno = %d",
128  fname, errno );
129  return SYS_MALLOC_ERR;
130  }
131 
132  /* concatenate the input parameters */
133  bufp = buffer;
134  if ( username && strlen( username ) ) {
135  memcpy( bufp, username, strlen( username ) );
136  bufp += strlen( username );
137  }
138  memcpy( bufp, &uid, sizeof( uid ) );
139  bufp += sizeof( uid );
140  if ( challenge ) {
141  memcpy( bufp, challenge, CHALLENGE_LEN );
142  bufp += CHALLENGE_LEN;
143  }
144  if ( key ) {
145  memcpy( bufp, key, key_len );
146  }
147 
148  /* generate an MD5 hash of the buffer, and copy it
149  to the output parameter authenticator */
150  MD5_Init( &ctx );
151  MD5_Update( &ctx, ( unsigned char* )buffer, buflen );
152  MD5_Final( ( unsigned char* )md5digest, &ctx );
153  memcpy( authenticator, md5digest, 16 );
154 
155  free( buffer );
156  return 0;
157  }
158 
159  /*
160  * osauthGetKey - read key from OS_AUTH_KEYFILE
161  *
162  * The return parameters include a malloc'ed buffer in
163  * *key that is the responsibility of the caller to free.
164  * The length of the buffer is in *key_len.
165  */
166  int
167  osauthGetKey( char **key, int *key_len ) {
168  static char fname[] = "osauthGetKey";
169  const char *keyfile;
170  int buflen, key_fd, nb;
171 
172  if ( key == NULL || key_len == NULL ) {
173  return USER__NULL_INPUT_ERR;
174  }
175 
176  boost::filesystem::path default_os_auth_keyfile_path = irods::get_irods_root_directory();
177  default_os_auth_keyfile_path.append(OS_AUTH_KEYFILE);
178  std::string default_os_auth_keyfile_path_string = default_os_auth_keyfile_path.string();
179 
180  keyfile = getenv( "irodsOsAuthKeyfile" );
181  if ( keyfile == NULL || *keyfile == '\0' ) {
182  keyfile = default_os_auth_keyfile_path_string.c_str();
183  }
184 
185  key_fd = open( keyfile, O_RDONLY, 0 );
186  if ( key_fd < 0 ) {
188  "%s: couldn't open %s for reading. errno = %d",
189  fname, keyfile, errno );
190  return FILE_OPEN_ERR;
191  }
192  off_t lseek_return = lseek( key_fd, 0, SEEK_END );
193  int errsv = errno;
194  if ( ( off_t ) - 1 == lseek_return ) {
195  fprintf( stderr, "SEEK_END lseek failed with error %d.\n", errsv );
196  close( key_fd );
197  return UNIX_FILE_LSEEK_ERR;
198  }
199  if ( lseek_return > std::numeric_limits<long long>::max() ) {
200  fprintf( stderr, "file of size %ju is too large for a long long.\n", ( uintmax_t )lseek_return );
201  close( key_fd );
202  return UNIX_FILE_LSEEK_ERR;
203  }
204  buflen = lseek_return;
205  lseek_return = lseek( key_fd, 0, SEEK_SET );
206  errsv = errno;
207  if ( ( off_t ) - 1 == lseek_return ) {
208  fprintf( stderr, "SEEK_SET lseek failed with error %d.\n", errsv );
209  close( key_fd );
210  return UNIX_FILE_LSEEK_ERR;
211  }
212 
213  char * keybuf = ( char* )malloc( buflen );
214  if ( keybuf == NULL ) {
216  "%s: could not allocate memory for key buffer. errno = %d",
217  fname, errno );
218  close( key_fd );
219  return SYS_MALLOC_ERR;
220  }
221  nb = read( key_fd, keybuf, buflen );
222  close( key_fd );
223  if ( nb < 0 ) {
225  "%s: couldn't read key from %s. errno = %d",
226  fname, keyfile, errno );
227  free( keybuf );
228  return FILE_READ_ERR;
229  }
230 
231  *key_len = buflen;
232  *key = keybuf;
233 
234  return 0;
235  }
236 
237  /*
238  * osauthGetAuth - this function runs the OS_AUTH_CMD command to
239  * retrieve an authenticator for the calling user.
240  */
241  int
242  osauthGetAuth( char *challenge,
243  char *username,
244  char *authenticator,
245  int authenticator_buflen ) {
246  static char fname[] = "osauthGetAuth";
247  int pipe1[2], pipe2[2];
248  int childStatus = 0;
249  int child_stdin, child_stdout, nb;
250  int buflen, challenge_len = CHALLENGE_LEN;
251  char buffer[128];
252 
253  if ( challenge == NULL || username == NULL || authenticator == NULL ) {
254  return USER__NULL_INPUT_ERR;
255  }
256 
257  if ( pipe( pipe1 ) < 0 ) {
258  rodsLog( LOG_ERROR, "%s: pipe1 create failed. errno = %d",
259  fname, errno );
260  return SYS_PIPE_ERROR - errno;
261  }
262  if ( pipe( pipe2 ) < 0 ) {
263  rodsLog( LOG_ERROR, "%s: pipe2 create failed. errno = %d",
264  fname, errno );
265  close( pipe1[0] );
266  close( pipe1[1] );
267  return SYS_PIPE_ERROR - errno;
268  }
269 
270  boost::filesystem::path os_auth_command_path = irods::get_irods_root_directory();
271  os_auth_command_path.append(OS_AUTH_CMD);
272 
273  pid_t childPid;
274 
275  posix_spawnattr_t attr;
276  posix_spawnattr_init(&attr);
277 
278  posix_spawn_file_actions_t file_actions;
279  posix_spawn_file_actions_init(&file_actions);
280  posix_spawn_file_actions_addclose(&file_actions, pipe1[1]);
281  posix_spawn_file_actions_addclose(&file_actions, pipe2[0]);
282  posix_spawn_file_actions_adddup2(&file_actions, pipe1[0], 0);
283  posix_spawn_file_actions_adddup2(&file_actions, pipe2[1], 1);
284 
285  std::string os_auth_command_path_string(os_auth_command_path.string());
286  char * argv[] = {&os_auth_command_path_string[0], nullptr};
287 
288  char ** environ_end;
289  for (environ_end = environ; *environ_end != nullptr; ++environ_end);
290  std::vector<char *> envp(environ, environ_end);
291  std::string env_var(OS_AUTH_ENV_USER "=");
292  env_var.append(username);
293  envp.back() = &env_var[0];
294  envp.push_back(nullptr);
295 
296  int status = posix_spawn(&childPid, os_auth_command_path_string.c_str(), &file_actions, &attr, &argv[0], &envp[0]);
297  posix_spawnattr_destroy(&attr);
298  posix_spawn_file_actions_destroy(&file_actions);
299  if (status != 0) {
300  rodsLog( LOG_ERROR, "%s: posix_spawn failed. errno = %d",
301  fname, errno );
302  close( pipe1[0] );
303  close( pipe1[1] );
304  close( pipe2[0] );
305  close( pipe2[1] );
306  return SYS_FORK_ERROR;
307  }
308  /* in the parent process */
309  close( pipe1[0] );
310  child_stdin = pipe1[1]; /* parent writes to child's stdin */
311  close( pipe2[1] );
312  child_stdout = pipe2[0]; /* parent reads from child's stdout */
313 
314  /* send the challenge to the OS_AUTH_CMD program on its stdin */
315  nb = write( child_stdin, &challenge_len, sizeof( challenge_len ) );
316  if ( nb < 0 ) {
318  "%s: error writing challenge_len to %s. errno = %d",
319  fname, os_auth_command_path.string().c_str(), errno );
320  close( child_stdin );
321  close( child_stdout );
322  return SYS_PIPE_ERROR - errno;
323  }
324  nb = write( child_stdin, challenge, challenge_len );
325  if ( nb < 0 ) {
327  "%s: error writing challenge to %s. errno = %d",
328  fname, os_auth_command_path.string().c_str(), errno );
329  close( child_stdin );
330  close( child_stdout );
331  return SYS_PIPE_ERROR - errno;
332  }
333 
334  /* read the response */
335  buflen = read( child_stdout, buffer, 128 );
336  if ( buflen < 0 ) {
337  rodsLog( LOG_ERROR, "%s: error reading from %s. errno = %d",
338  fname, os_auth_command_path.string().c_str(), errno );
339  close( child_stdin );
340  close( child_stdout );
341  return SYS_PIPE_ERROR - errno;
342  }
343 
344  close( child_stdin );
345  close( child_stdout );
346 
347  if ( waitpid( childPid, &childStatus, 0 ) < 0 ) {
348  rodsLog( LOG_ERROR, "%s: waitpid error. errno = %d",
349  fname, errno );
350  return EXEC_CMD_ERROR;
351  }
352 
353  if ( WIFEXITED( childStatus ) ) {
354  if ( WEXITSTATUS( childStatus ) ) {
356  "%s: command failed: %s", fname, buffer );
357  return EXEC_CMD_ERROR;
358  }
359  }
360  else {
362  "%s: some error running %s", fname, os_auth_command_path.string().c_str() );
363  }
364 
365  /* authenticator is in buffer now */
366  if ( buflen > authenticator_buflen ) {
368  "%s: not enough space in return buffer for authenticator", fname );
370  }
371  memcpy( authenticator, buffer, buflen );
372 
373  return 0;
374  }
375 
376  /*
377  * osauthGetUid - looks up the given username using getpwnam
378  * and returns the user's uid if successful, or -1 if not.
379  */
380  int
381  osauthGetUid( char *username ) {
382  if ( NULL == username ) {
383  rodsLog( LOG_ERROR, "Error: osauthGetUid called with null username argument." );
384  return -1;
385  }
386  static char fname[] = "osauthGetUid";
387 
388  errno = 0;
389  struct passwd *pwent = getpwnam( username );
390  if ( pwent == NULL ) {
391  if ( errno ) {
393  "%s: error calling getpwnam for %s. errno = %d",
394  fname, username, errno );
395  return -1;
396  }
397  else {
399  "%s: no such user %s", fname, username );
400  return -1;
401  }
402  }
403  return pwent->pw_uid;
404  }
405 
406  /*
407  * osauthGetUsername - looks up the user identified by
408  * the uid determined by getuid(). Places the username
409  * in the provided username buffer and returns the uid
410  * on success, or -1 if not.
411  */
412  int
413  osauthGetUsername( char *username, int username_len ) {
414  static char fname[] = "osauthGetUsername";
415  struct passwd *pwent;
416  int uid;
417 
418  uid = getuid();
419  errno = 0;
420  pwent = getpwuid( uid );
421  if ( pwent == NULL ) {
422  if ( errno ) {
424  "%s: error calling getpwuid for uid %d. errno = %d",
425  fname, uid, errno );
426  return -1;
427  }
428  else {
429  rodsLog( LOG_ERROR, "%s: no user with uid %d",
430  fname, uid );
431  return -1;
432  }
433  }
434  if ( ( unsigned int )username_len <= strlen( pwent->pw_name ) ) {
435  rodsLog( LOG_ERROR, "%s: username input buffer too small (%d <= %d)",
436  fname, username_len, strlen( pwent->pw_name ) );
437  return -1;
438  }
439  strcpy( username, pwent->pw_name );
440 
441  return uid;
442  }
443 
444 } // extern "C"
rodsLog
void rodsLog(int level, const char *formatStr,...)
Definition: rodsLog.cpp:86
UNIX_FILE_LSEEK_ERR
@ UNIX_FILE_LSEEK_ERR
Definition: rodsErrorTable.h:308
NULL
#define NULL
Definition: rodsDef.h:70
osauth.h
osauthGenerateAuthenticator
int osauthGenerateAuthenticator(char *username, int uid, char *challenge, char *key, int key_len, char *authenticator, int authenticator_len)
Definition: osauth.cpp:107
SYS_MALLOC_ERR
@ SYS_MALLOC_ERR
Definition: rodsErrorTable.h:84
OS_AUTH_ENV_USER
#define OS_AUTH_ENV_USER
Definition: osauth.h:10
authenticate.h
osauthVerifyResponse
int osauthVerifyResponse(char *challenge, char *username, char *response)
Definition: osauth.cpp:36
irods_default_paths.hpp
rcGlobalExtern.h
LOG_ERROR
#define LOG_ERROR
Definition: rodsLog.h:43
EXEC_CMD_OUTPUT_TOO_LARGE
@ EXEC_CMD_OUTPUT_TOO_LARGE
Definition: rodsErrorTable.h:264
MAX_PASSWORD_LEN
#define MAX_PASSWORD_LEN
Definition: authenticate.h:9
OS_AUTH_CMD
#define OS_AUTH_CMD
Definition: osauth.h:9
osauthGetUid
int osauthGetUid(char *username)
Definition: osauth.cpp:381
irods::get_irods_root_directory
boost::filesystem::path get_irods_root_directory()
Definition: irods_default_paths.cpp:8
EXEC_CMD_ERROR
@ EXEC_CMD_ERROR
Definition: rodsErrorTable.h:265
environ
char ** environ
osauthGetAuth
int osauthGetAuth(char *challenge, char *username, char *authenticator, int authenticator_buflen)
Definition: osauth.cpp:242
SYS_FORK_ERROR
@ SYS_FORK_ERROR
Definition: rodsErrorTable.h:116
irods.pypyodbc.buffer
buffer
Definition: pypyodbc.py:46
irods.pypyodbc.status
status
Definition: pypyodbc.py:467
osauthGetKey
int osauthGetKey(char **key, int *key_len)
Definition: osauth.cpp:167
FILE_READ_ERR
@ FILE_READ_ERR
Definition: rodsErrorTable.h:495
SYS_PIPE_ERROR
@ SYS_PIPE_ERROR
Definition: rodsErrorTable.h:117
CAT_INVALID_AUTHENTICATION
@ CAT_INVALID_AUTHENTICATION
Definition: rodsErrorTable.h:441
RESPONSE_LEN
#define RESPONSE_LEN
Definition: authenticate.h:11
USER_INPUT_OPTION_ERR
@ USER_INPUT_OPTION_ERR
Definition: rodsErrorTable.h:249
rodsLogError
void rodsLogError(int level, int errCode, char *formatStr,...)
Definition: rodsLog.cpp:422
osauthGetUsername
int osauthGetUsername(char *username, int username_len)
Definition: osauth.cpp:413
FILE_OPEN_ERR
@ FILE_OPEN_ERR
Definition: rodsErrorTable.h:494
CHALLENGE_LEN
#define CHALLENGE_LEN
Definition: authenticate.h:10
SYS_USER_RETRIEVE_ERR
@ SYS_USER_RETRIEVE_ERR
Definition: rodsErrorTable.h:187
OS_AUTH_KEYFILE
#define OS_AUTH_KEYFILE
Definition: osauth.h:11
rods.h
USER__NULL_INPUT_ERR
@ USER__NULL_INPUT_ERR
Definition: rodsErrorTable.h:247