(root)/
Linux-PAM-1.5.3/
modules/
pam_timestamp/
hmacsha1.c
       1  /* An implementation of HMAC using SHA-1.
       2   *
       3   * Copyright (c) 2003 Red Hat, Inc.
       4   * Written by Nalin Dahyabhai <nalin@redhat.com>
       5   *
       6   * Redistribution and use in source and binary forms, with or without
       7   * modification, are permitted provided that the following conditions
       8   * are met:
       9   * 1. Redistributions of source code must retain the above copyright
      10   *    notice, and the entire permission notice in its entirety,
      11   *    including the disclaimer of warranties.
      12   * 2. Redistributions in binary form must reproduce the above copyright
      13   *    notice, this list of conditions and the following disclaimer in the
      14   *    documentation and/or other materials provided with the distribution.
      15   * 3. The name of the author may not be used to endorse or promote
      16   *    products derived from this software without specific prior
      17   *    written permission.
      18   *
      19   * ALTERNATIVELY, this product may be distributed under the terms of
      20   * the GNU Public License, in which case the provisions of the GPL are
      21   * required INSTEAD OF the above restrictions.  (This clause is
      22   * necessary due to a potential bad interaction between the GPL and
      23   * the restrictions contained in a BSD-style copyright.)
      24   *
      25   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
      26   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
      27   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
      28   * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
      29   * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
      30   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
      31   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
      32   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
      33   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
      34   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
      35   * OF THE POSSIBILITY OF SUCH DAMAGE.
      36   *
      37   */
      38  /* See RFC 2104 for descriptions. */
      39  #include "config.h"
      40  #include <sys/types.h>
      41  #include <sys/stat.h>
      42  #include <errno.h>
      43  #include <fcntl.h>
      44  #include <grp.h>
      45  #include <pwd.h>
      46  #include <stdlib.h>
      47  #include <string.h>
      48  #include <unistd.h>
      49  #include <syslog.h>
      50  #include <security/pam_ext.h>
      51  #include "pam_inline.h"
      52  #include "hmacsha1.h"
      53  #include "sha1.h"
      54  
      55  #define MINIMUM_KEY_SIZE SHA1_OUTPUT_SIZE
      56  #define MAXIMUM_KEY_SIZE SHA1_BLOCK_SIZE
      57  
      58  static void
      59  hmac_key_create(pam_handle_t *pamh, const char *filename, size_t key_size,
      60  		uid_t owner, gid_t group)
      61  {
      62  	int randfd, keyfd, i;
      63  	size_t count;
      64  	char *key;
      65  
      66  	/* Open the destination file. */
      67  	keyfd = open(filename,
      68  		     O_WRONLY | O_CREAT | O_EXCL | O_TRUNC,
      69  		     S_IRUSR | S_IWUSR);
      70  	if (keyfd == -1) {
      71  		pam_syslog(pamh, LOG_ERR, "Cannot create %s: %m", filename);
      72  		return;
      73  	}
      74  
      75  
      76  	 if (fchown(keyfd, owner, group) == -1) {
      77  		pam_syslog(pamh, LOG_ERR, "Cannot chown %s: %m", filename);
      78  		close(keyfd);
      79  		return;
      80  	}
      81  
      82  	/* Open the random device to get key data. */
      83  	randfd = open("/dev/urandom", O_RDONLY);
      84  	if (randfd == -1) {
      85  		pam_syslog(pamh, LOG_ERR, "Cannot open /dev/urandom: %m");
      86  		close(keyfd);
      87  		return;
      88  	}
      89  
      90  	/* Read random data for use as the key. */
      91  	key = malloc(key_size);
      92  	count = 0;
      93  	if (!key) {
      94  		close(keyfd);
      95  		close(randfd);
      96  		return;
      97  	}
      98  	while (count < key_size) {
      99  		i = read(randfd, key + count, key_size - count);
     100  		if ((i == 0) || (i == -1)) {
     101  			break;
     102  		}
     103  		count += i;
     104  	}
     105  
     106  	close(randfd);
     107  
     108  	/* If we didn't get enough, stop here. */
     109  	if (count < key_size) {
     110  		pam_syslog(pamh, LOG_ERR, "Short read on random device");
     111  		pam_overwrite_n(key, key_size);
     112  		free(key);
     113  		close(keyfd);
     114  		return;
     115  	}
     116  
     117  	/* Now write the key. */
     118  	count = 0;
     119  	while (count < key_size) {
     120  		i = write(keyfd, key + count, key_size - count);
     121  		if ((i == 0) || (i == -1)) {
     122  			break;
     123  		}
     124  		count += i;
     125  	}
     126  	pam_overwrite_n(key, key_size);
     127  	free(key);
     128  	close(keyfd);
     129  }
     130  
     131  static void
     132  hmac_key_read(pam_handle_t *pamh, const char *filename, size_t default_key_size,
     133  	      uid_t owner, gid_t group,
     134  	      void **key, size_t *key_size)
     135  {
     136  	char *tmp;
     137  	int keyfd, i, count;
     138  	struct stat st;
     139  
     140  	tmp = NULL;
     141  	*key = NULL;
     142  	*key_size = 0;
     143  
     144  	/* Try to open the key file. */
     145  	keyfd = open(filename, O_RDONLY);
     146  	if (keyfd == -1) {
     147  		/* No such thing? Create it. */
     148  		if (errno == ENOENT) {
     149  			hmac_key_create(pamh, filename, default_key_size,
     150  					owner, group);
     151  			keyfd = open(filename, O_RDONLY);
     152  		} else {
     153  			pam_syslog(pamh, LOG_ERR, "Cannot open %s: %m", filename);
     154  		}
     155  		if (keyfd == -1)
     156  			return;
     157  	}
     158  
     159  	/* If we failed to open the file, we're done. */
     160  	if (fstat(keyfd, &st) == -1) {
     161  		close(keyfd);
     162  		return;
     163  	}
     164  
     165  	/* Read the contents of the file. */
     166  	tmp = malloc(st.st_size);
     167  	if (!tmp) {
     168  		close(keyfd);
     169  		return;
     170  	}
     171  
     172  	count = 0;
     173  	while (count < st.st_size) {
     174  		i = read(keyfd, tmp + count, st.st_size - count);
     175  		if ((i == 0) || (i == -1)) {
     176  			break;
     177  		}
     178  		count += i;
     179  	}
     180  	close(keyfd);
     181  
     182  	/* Require that we got the expected amount of data. */
     183  	if (count < st.st_size) {
     184  		pam_overwrite_n(tmp, st.st_size);
     185  		free(tmp);
     186  		return;
     187  	}
     188  
     189  	/* Pass the key back. */
     190  	*key = tmp;
     191  	*key_size = st.st_size;
     192  }
     193  
     194  static void
     195  xor_block(unsigned char *p, unsigned char byte, size_t length)
     196  {
     197  	size_t i;
     198  	for (i = 0; i < length; i++) {
     199  		p[i] = p[i] ^ byte;
     200  	}
     201  }
     202  
     203  void
     204  hmac_sha1_generate(void **mac, size_t *mac_length,
     205  		   const void *raw_key, size_t raw_key_size,
     206  		   const void *text, size_t text_length)
     207  {
     208  	unsigned char key[MAXIMUM_KEY_SIZE] = {}, tmp_key[MAXIMUM_KEY_SIZE];
     209  	size_t maximum_key_size = SHA1_BLOCK_SIZE,
     210  	       minimum_key_size = SHA1_OUTPUT_SIZE;
     211  	const unsigned char ipad = 0x36, opad = 0x5c;
     212  	struct sha1_context sha1;
     213  	unsigned char inner[SHA1_OUTPUT_SIZE], outer[SHA1_OUTPUT_SIZE];
     214  
     215  	*mac = NULL;
     216  	*mac_length = 0;
     217  
     218  #ifndef HMAC_ALLOW_SHORT_KEYS
     219  	/* If the key is too short, don't bother. */
     220  	if (raw_key_size < minimum_key_size) {
     221  		return;
     222  	}
     223  #endif
     224  
     225  	/* If the key is too long, "compress" it, else copy it and pad it
     226  	 * out with zero bytes. */
     227  	if (raw_key_size > maximum_key_size) {
     228  		sha1_init(&sha1);
     229  		sha1_update(&sha1, raw_key, raw_key_size);
     230  		sha1_output(&sha1, key);
     231  	} else {
     232  		memmove(key, raw_key, raw_key_size);
     233  	}
     234  
     235  	/* Generate the inner sum. */
     236  	memcpy(tmp_key, key, sizeof(tmp_key));
     237  	xor_block(tmp_key, ipad, sizeof(tmp_key));
     238  
     239  	sha1_init(&sha1);
     240  	sha1_update(&sha1, tmp_key, sizeof(tmp_key));
     241  	sha1_update(&sha1, text, text_length);
     242  	sha1_output(&sha1, inner);
     243  
     244  	/* Generate the outer sum. */
     245  	memcpy(tmp_key, key, sizeof(tmp_key));
     246  	xor_block(tmp_key, opad, sizeof(tmp_key));
     247  
     248  	sha1_init(&sha1);
     249  	sha1_update(&sha1, tmp_key, sizeof(tmp_key));
     250  	sha1_update(&sha1, inner, sizeof(inner));
     251  	sha1_output(&sha1, outer);
     252  
     253  	/* We don't need any of the keys any more. */
     254  	pam_overwrite_array(key);
     255  	pam_overwrite_array(tmp_key);
     256  
     257  	/* Allocate space to store the output. */
     258  	*mac_length = sizeof(outer);
     259  	*mac = malloc(*mac_length);
     260  	if (*mac == NULL) {
     261  		*mac_length = 0;
     262  		return;
     263  	}
     264  
     265  	memcpy(*mac, outer, *mac_length);
     266  }
     267  
     268  void
     269  hmac_sha1_generate_file(pam_handle_t *pamh, void **mac, size_t *mac_length,
     270  			const char *keyfile, uid_t owner, gid_t group,
     271  			const void *text, size_t text_length)
     272  {
     273  	void *key;
     274  	size_t key_length;
     275  
     276  	hmac_key_read(pamh, keyfile,
     277  		      MAXIMUM_KEY_SIZE, owner, group,
     278  		      &key, &key_length);
     279  	if (key == NULL) {
     280  		*mac = NULL;
     281  		*mac_length = 0;
     282  		return;
     283  	}
     284  	hmac_sha1_generate(mac, mac_length,
     285  			   key, key_length,
     286  			   text, text_length);
     287  	pam_overwrite_n(key, key_length);
     288  	free(key);
     289  }
     290  
     291  size_t
     292  hmac_sha1_size(void)
     293  {
     294  	return SHA1_OUTPUT_SIZE;
     295  }