xref: /illumos-gate/usr/src/cmd/ndmpd/ndmp/ndmpd_connect.c (revision 4c28a617e3922d92a58e813a5b955eb526b9c386)
1 /*
2  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /*
7  * BSD 3 Clause License
8  *
9  * Copyright (c) 2007, The Storage Networking Industry Association.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 	- Redistributions of source code must retain the above copyright
15  *	  notice, this list of conditions and the following disclaimer.
16  *
17  * 	- Redistributions in binary form must reproduce the above copyright
18  *	  notice, this list of conditions and the following disclaimer in
19  *	  the documentation and/or other materials provided with the
20  *	  distribution.
21  *
22  *	- Neither the name of The Storage Networking Industry Association (SNIA)
23  *	  nor the names of its contributors may be used to endorse or promote
24  *	  products derived from this software without specific prior written
25  *	  permission.
26  *
27  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
28  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
31  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37  * POSSIBILITY OF SUCH DAMAGE.
38  */
39 /* Copyright (c) 2007, The Storage Networking Industry Association. */
40 /* Copyright (c) 1996, 1997 PDC, Network Appliance. All Rights Reserved */
41 /* Copyright 2014 Nexenta Systems, Inc. All rights reserved. */
42 
43 #include <sys/types.h>
44 #include <errno.h>
45 #include <pwd.h>
46 #include <sys/socket.h>
47 #include <netinet/in.h>
48 #include <sys/queue.h>
49 #include <arpa/inet.h>
50 #include <md5.h>
51 #include <shadow.h>
52 #include <crypt.h>
53 #include <alloca.h>
54 #include "ndmpd_common.h"
55 #include "ndmpd.h"
56 #include <libndmp.h>
57 #include <ndmpd_door.h>
58 #include <security/pam_appl.h>
59 
60 
61 static int ndmpd_connect_auth_text(char *uname, char *auth_id,
62     char *auth_password);
63 static int ndmpd_connect_auth_md5(char *uname, char *auth_id, char *auth_digest,
64     unsigned char *auth_challenge);
65 static struct conn_list *ndmp_connect_list_find(ndmp_connection_t *connection);
66 static void create_md5_digest(unsigned char *digest, char *passwd,
67     unsigned char *challenge);
68 static struct conn_list *ndmp_connect_list_find_id(int id);
69 
70 /* routines for connection info */
71 void ndmp_connect_list_get(ndmp_door_ctx_t *enc_ctx);
72 static void connection_get(struct conn_list *clp, ndmp_door_ctx_t *enc_ctx);
73 static void ndmp_connect_get_conn(struct conn_list *clp,
74     ndmp_door_ctx_t *enc_ctx);
75 static void ndmp_connect_get_v2(ndmp_connection_t *connection,
76     ndmp_door_ctx_t *enc_ctx);
77 static void ndmp_connect_get_scsi_v2(ndmpd_session_t *session,
78     ndmp_door_ctx_t *enc_ctx);
79 static void ndmp_connect_get_tape_v2(ndmpd_session_t *session,
80     ndmp_door_ctx_t *enc_ctx);
81 static void ndmp_connect_get_mover_v2(ndmpd_session_t *session,
82     ndmp_door_ctx_t *enc_ctx);
83 static void ndmp_connect_get_data_v2(ndmpd_session_t *session,
84     ndmp_door_ctx_t *enc_ctx);
85 static void ndmp_connect_get_v3(ndmp_connection_t *connection,
86     ndmp_door_ctx_t *enc_ctx);
87 static void ndmp_connect_get_mover_v3(ndmpd_session_t *session,
88     ndmp_door_ctx_t *enc_ctx);
89 static void ndmp_connect_get_data_v3(ndmpd_session_t *session,
90     ndmp_door_ctx_t *enc_ctx);
91 void ndmpd_get_devs(ndmp_door_ctx_t *enc_ctx);
92 
93 #ifndef LIST_FOREACH
94 #define	LIST_FOREACH(var, head, field)					\
95 	for ((var) = (head)->lh_first; (var); (var) = (var)->field.le_next)
96 #endif /* LIST_FOREACH */
97 
98 /*
99  * List of active connections.
100  */
101 struct conn_list {
102 	LIST_ENTRY(conn_list) cl_q;
103 	int cl_id;
104 	ndmp_connection_t *cl_conn;
105 };
106 LIST_HEAD(cl_head, conn_list);
107 
108 /*
109  * Head of the active connections.
110  */
111 static struct cl_head cl_head;
112 
113 mutex_t cl_mutex = DEFAULTMUTEX;
114 
115 
116 /*
117  * Set this variable to non-zero to print verbose information.
118  */
119 int ndmp_connect_print_verbose = 0;
120 
121 
122 /*
123  * ************************************************************************
124  * NDMP V2 HANDLERS
125  * ************************************************************************
126  */
127 
128 /*
129  * ndmpd_connect_open_v2
130  *
131  * This handler sets the protocol version to be used on the connection.
132  *
133  * Parameters:
134  *   connection (input) - connection handle.
135  *   body       (input) - request message body.
136  *
137  * Returns:
138  *   void
139  */
140 
141 void
142 ndmpd_connect_open_v2(ndmp_connection_t *connection, void *body)
143 {
144 	ndmp_connect_open_request *request = (ndmp_connect_open_request *)body;
145 	ndmp_connect_open_reply reply;
146 	ndmpd_session_t *session;
147 
148 	reply.error = NDMP_NO_ERR;
149 
150 	if (!(session = (ndmpd_session_t *)ndmp_get_client_data(connection)))
151 		return;
152 
153 	if (session->ns_mover.md_state != NDMP_MOVER_STATE_IDLE ||
154 	    session->ns_data.dd_state != NDMP_DATA_STATE_IDLE)
155 		reply.error = NDMP_ILLEGAL_STATE_ERR;
156 	else if (request->protocol_version > ndmp_ver)
157 		reply.error = NDMP_ILLEGAL_ARGS_ERR;
158 
159 	ndmp_send_reply(connection, (void *) &reply,
160 	    "sending connect_open reply");
161 
162 	/*
163 	 * Set the protocol version.
164 	 * Must wait until after sending the reply since the reply
165 	 * must be sent using the same protocol version that was used
166 	 * to process the request.
167 	 */
168 	if (reply.error == NDMP_NO_ERR) {
169 		NDMP_LOG(LOG_DEBUG, "set ver to: %d",
170 		    request->protocol_version);
171 		ndmp_set_version(connection, request->protocol_version);
172 		session->ns_protocol_version = request->protocol_version;
173 	}
174 }
175 
176 
177 /*
178  * ndmpd_connect_client_auth_v2
179  *
180  * This handler authorizes the NDMP connection.
181  *
182  * Parameters:
183  *   connection (input) - connection handle.
184  *   msginfo    (input) - request message.
185  *
186  * Returns:
187  *   void
188  */
189 void
190 ndmpd_connect_client_auth_v2(ndmp_connection_t *connection, void *body)
191 {
192 	ndmp_connect_client_auth_request *request;
193 	ndmp_connect_client_auth_reply reply;
194 	ndmp_auth_text *auth;
195 	ndmpd_session_t *session;
196 	ndmp_auth_md5 *md5;
197 	unsigned char md5_digest[16];
198 	char *passwd, *dec_passwd;
199 	char *uname;
200 
201 	request = (ndmp_connect_client_auth_request *)body;
202 	NDMP_LOG(LOG_DEBUG, "auth_type:%s",
203 	    request->auth_data.auth_type == NDMP_AUTH_NONE ? "None" :
204 	    (request->auth_data.auth_type == NDMP_AUTH_TEXT ? "Text" :
205 	    (request->auth_data.auth_type == NDMP_AUTH_MD5 ? "MD5" :
206 	    "Invalid")));
207 
208 	reply.error = NDMP_NO_ERR;
209 
210 	switch (request->auth_data.auth_type) {
211 	case NDMP_AUTH_NONE:
212 		/*
213 		 * Allow no authorization for development.
214 		 * Comment the following for a non-secure production server.
215 		 */
216 		NDMP_LOG(LOG_ERR, "Authorization denied.");
217 		NDMP_LOG(LOG_ERR,
218 		    "Authorization type should be md5 or cleartext.");
219 		reply.error = NDMP_ILLEGAL_ARGS_ERR;
220 		ndmpd_audit_connect(connection, EINVAL);
221 		break;
222 
223 	case NDMP_AUTH_TEXT:
224 		/* Check authorization.  */
225 		if ((uname = ndmpd_get_prop(NDMP_CLEARTEXT_USERNAME)) == NULL ||
226 		    *uname == 0) {
227 			NDMP_LOG(LOG_ERR, "Authorization denied.");
228 			NDMP_LOG(LOG_ERR, "User name is not set at server.");
229 			reply.error = NDMP_NOT_AUTHORIZED_ERR;
230 			ndmp_set_authorized(connection, FALSE);
231 			ndmp_send_reply(connection, (void *) &reply,
232 			    "sending ndmp_connect_client_auth reply");
233 			ndmpd_audit_connect(connection,
234 			    ADT_FAIL_PAM + PAM_AUTH_ERR);
235 			return;
236 		}
237 		auth = &request->auth_data.ndmp_auth_data_u.auth_text;
238 		if (strcmp(uname, auth->user) != 0) {
239 			NDMP_LOG(LOG_ERR,
240 			    "Authorization denied. Not a valid user.");
241 			reply.error = NDMP_NOT_AUTHORIZED_ERR;
242 			ndmpd_audit_connect(connection,
243 			    ADT_FAIL_PAM + PAM_AUTH_ERR);
244 			break;
245 		}
246 		passwd = ndmpd_get_prop(NDMP_CLEARTEXT_PASSWORD);
247 		if (!passwd || !*passwd) {
248 			NDMP_LOG(LOG_ERR, "Authorization denied.");
249 			NDMP_LOG(LOG_ERR,
250 			    "Cleartext password is not set at server.");
251 			reply.error = NDMP_NOT_AUTHORIZED_ERR;
252 			ndmp_set_authorized(connection, FALSE);
253 			ndmp_send_reply(connection, (void *) &reply,
254 			    "sending ndmp_connect_client_auth reply");
255 			ndmpd_audit_connect(connection,
256 			    ADT_FAIL_PAM + PAM_AUTH_ERR);
257 			return;
258 		} else {
259 			dec_passwd = ndmp_base64_decode(passwd);
260 		}
261 		if (!dec_passwd || !*dec_passwd ||
262 		    strcmp(auth->password, dec_passwd) != 0) {
263 			NDMP_LOG(LOG_ERR,
264 			    "Authorization denied. Invalid password.");
265 			reply.error = NDMP_NOT_AUTHORIZED_ERR;
266 		} else {
267 			NDMP_LOG(LOG_DEBUG, "Authorization granted.");
268 		}
269 		ndmpd_audit_connect(connection, reply.error ?
270 		    ADT_FAIL_PAM + PAM_AUTH_ERR : 0);
271 
272 		free(dec_passwd);
273 		break;
274 
275 	case NDMP_AUTH_MD5:
276 		/* Check authorization.  */
277 		if ((uname = ndmpd_get_prop(NDMP_CRAM_MD5_USERNAME)) == NULL ||
278 		    *uname == 0) {
279 			NDMP_LOG(LOG_ERR, "Authorization denied.");
280 			NDMP_LOG(LOG_ERR,  "User name is not set at server.");
281 			reply.error = NDMP_NOT_AUTHORIZED_ERR;
282 			ndmp_set_authorized(connection, FALSE);
283 			ndmp_send_reply(connection, (void *) &reply,
284 			    "sending ndmp_connect_client_auth reply");
285 			ndmpd_audit_connect(connection,
286 			    ADT_FAIL_PAM + PAM_AUTH_ERR);
287 			return;
288 		}
289 		md5 = &request->auth_data.ndmp_auth_data_u.auth_md5;
290 		passwd = ndmpd_get_prop(NDMP_CRAM_MD5_PASSWORD);
291 		if (!passwd || !*passwd) {
292 			NDMP_LOG(LOG_ERR, "Authorization denied.");
293 			NDMP_LOG(LOG_ERR, "MD5 password is not set at server.");
294 			reply.error = NDMP_NOT_AUTHORIZED_ERR;
295 			ndmp_set_authorized(connection, FALSE);
296 			ndmp_send_reply(connection, (void *) &reply,
297 			    "sending ndmp_connect_client_auth reply");
298 			ndmpd_audit_connect(connection,
299 			    ADT_FAIL_PAM + PAM_AUTH_ERR);
300 			return;
301 		} else {
302 			dec_passwd = ndmp_base64_decode(passwd);
303 		}
304 		session = ndmp_get_client_data(connection);
305 		create_md5_digest(md5_digest, dec_passwd,
306 		    session->ns_challenge);
307 
308 		if (strcmp(uname, md5->user) != 0) {
309 			NDMP_LOG(LOG_ERR,
310 			    "Authorization denied. Not a valid user.");
311 			reply.error = NDMP_NOT_AUTHORIZED_ERR;
312 		} else if (memcmp(md5_digest, md5->auth_digest,
313 		    sizeof (md5_digest)) != 0) {
314 			NDMP_LOG(LOG_ERR,
315 			    "Authorization denied. Invalid password.");
316 			reply.error = NDMP_NOT_AUTHORIZED_ERR;
317 		} else {
318 			NDMP_LOG(LOG_DEBUG, "Authorization granted");
319 		}
320 		ndmpd_audit_connect(connection, reply.error ?
321 		    ADT_FAIL_PAM + PAM_AUTH_ERR : 0);
322 
323 		free(dec_passwd);
324 		break;
325 
326 	default:
327 		reply.error = NDMP_ILLEGAL_ARGS_ERR;
328 	}
329 
330 	if (reply.error == NDMP_NO_ERR)
331 		ndmp_set_authorized(connection, TRUE);
332 	else
333 		ndmp_set_authorized(connection, FALSE);
334 
335 	ndmp_send_reply(connection, (void *) &reply,
336 	    "sending ndmp_connect_client_auth reply");
337 }
338 
339 
340 /*
341  * ndmpd_connect_server_auth_v2
342  *
343  * This handler authenticates the server to the client.
344  *
345  * Parameters:
346  *   connection (input) - connection handle.
347  *   msginfo    (input) - request message.
348  *
349  * Returns:
350  *   void
351  */
352 void
353 ndmpd_connect_server_auth_v2(ndmp_connection_t *connection, void *body)
354 {
355 	ndmp_connect_server_auth_request *request;
356 	ndmp_connect_server_auth_reply reply;
357 
358 	request = (ndmp_connect_server_auth_request *)body;
359 
360 	NDMP_LOG(LOG_DEBUG, "auth_type:%s",
361 	    request->client_attr.auth_type == NDMP_AUTH_NONE ? "None" :
362 	    (request->client_attr.auth_type == NDMP_AUTH_TEXT ? "Text" :
363 	    (request->client_attr.auth_type == NDMP_AUTH_MD5 ? "MD5" :
364 	    "Invalid")));
365 
366 	reply.error = NDMP_NO_ERR;
367 	reply.auth_result.auth_type = request->client_attr.auth_type;
368 	switch (request->client_attr.auth_type) {
369 	case NDMP_AUTH_NONE:
370 		break;
371 
372 	case NDMP_AUTH_TEXT:
373 		reply.auth_result.ndmp_auth_data_u.auth_text.user = "ndmpd";
374 		reply.auth_result.ndmp_auth_data_u.auth_text.password =
375 		    "ndmpsdk";
376 		break;
377 
378 	case NDMP_AUTH_MD5:
379 		reply.error = NDMP_ILLEGAL_ARGS_ERR;
380 		break;
381 
382 	default:
383 		reply.error = NDMP_ILLEGAL_ARGS_ERR;
384 	}
385 
386 	ndmp_send_reply(connection, (void *) &reply,
387 	    "sending ndmp_connect_auth reply");
388 }
389 
390 
391 /*
392  * ndmpd_connect_close_v2
393  *
394  * This handler closes the connection.
395  *
396  * Parameters:
397  *   connection (input) - connection handle.
398  *   msginfo    (input) - request message.
399  *
400  * Returns:
401  *   void
402  */
403 /*ARGSUSED*/
404 void
405 ndmpd_connect_close_v2(ndmp_connection_t *connection, void *body)
406 {
407 	ndmpd_session_t *session;
408 
409 	if ((session = (ndmpd_session_t *)ndmp_get_client_data(connection))) {
410 		(void) ndmp_close(connection);
411 		session->ns_eof = TRUE;
412 	}
413 }
414 
415 /*
416  * ************************************************************************
417  * NDMP V3 HANDLERS
418  * ************************************************************************
419  */
420 
421 /*
422  * ndmpd_connect_client_auth_v3
423  *
424  * This handler authorizes the NDMP connection.
425  *
426  * Parameters:
427  *   connection (input) - connection handle.
428  *   msginfo    (input) - request message.
429  *
430  * Returns:
431  *   void
432  */
433 void
434 ndmpd_connect_client_auth_v3(ndmp_connection_t *connection, void *body)
435 {
436 	ndmp_connect_client_auth_request_v3 *request;
437 	ndmp_connect_client_auth_reply_v3 reply;
438 	ndmp_auth_text_v3 *auth;
439 	ndmpd_session_t *session;
440 	ndmp_auth_md5_v3 *md5;
441 	struct in_addr addr;
442 	char *uname;
443 	char *type;
444 
445 	request = (ndmp_connect_client_auth_request_v3 *)body;
446 	NDMP_LOG(LOG_DEBUG, "auth_type %s",
447 	    request->auth_data.auth_type == NDMP_AUTH_NONE ? "None" :
448 	    request->auth_data.auth_type == NDMP_AUTH_TEXT ? "Text" :
449 	    request->auth_data.auth_type == NDMP_AUTH_MD5 ? "MD5" : "Invalid");
450 
451 	reply.error = NDMP_NO_ERR;
452 
453 	switch (request->auth_data.auth_type) {
454 	case NDMP_AUTH_NONE:
455 		type = "none";
456 		reply.error = NDMP_NOT_SUPPORTED_ERR;
457 		ndmpd_audit_connect(connection, ENOTSUP);
458 		break;
459 
460 	case NDMP_AUTH_TEXT:
461 		/* Check authorization.  */
462 		if ((uname = ndmpd_get_prop(NDMP_CLEARTEXT_USERNAME)) == NULL ||
463 		    *uname == 0) {
464 			NDMP_LOG(LOG_ERR, "Authorization denied.");
465 			NDMP_LOG(LOG_ERR, "User name is not set at server.");
466 			reply.error = NDMP_NOT_AUTHORIZED_ERR;
467 			ndmp_set_authorized(connection, FALSE);
468 			ndmp_send_reply(connection, (void *) &reply,
469 			    "sending ndmp_connect_client_auth reply");
470 			ndmpd_audit_connect(connection,
471 			    ADT_FAIL_PAM + PAM_AUTH_ERR);
472 			return;
473 		}
474 		type = "text";
475 		auth = &request->auth_data.ndmp_auth_data_v3_u.auth_text;
476 		reply.error = ndmpd_connect_auth_text(uname, auth->auth_id,
477 		    auth->auth_password);
478 		ndmpd_audit_connect(connection, reply.error ?
479 		    ADT_FAIL_PAM + PAM_AUTH_ERR : 0);
480 		break;
481 
482 	case NDMP_AUTH_MD5:
483 		/* Check authorization.  */
484 		if ((uname = ndmpd_get_prop(NDMP_CRAM_MD5_USERNAME)) == NULL ||
485 		    *uname == 0) {
486 			NDMP_LOG(LOG_ERR, "Authorization denied.");
487 			NDMP_LOG(LOG_ERR, "User name is not set at server.");
488 			reply.error = NDMP_NOT_AUTHORIZED_ERR;
489 			ndmp_set_authorized(connection, FALSE);
490 			ndmp_send_reply(connection, (void *) &reply,
491 			    "sending ndmp_connect_client_auth reply");
492 			ndmpd_audit_connect(connection,
493 			    ADT_FAIL_PAM + PAM_AUTH_ERR);
494 			return;
495 		}
496 		type = "md5";
497 		session = ndmp_get_client_data(connection);
498 		md5 = &request->auth_data.ndmp_auth_data_v3_u.auth_md5;
499 		reply.error = ndmpd_connect_auth_md5(uname, md5->auth_id,
500 		    md5->auth_digest, session->ns_challenge);
501 		ndmpd_audit_connect(connection, reply.error ?
502 		    ADT_FAIL_PAM + PAM_AUTH_ERR : 0);
503 		break;
504 
505 	default:
506 		type = "unknown";
507 		reply.error = NDMP_ILLEGAL_ARGS_ERR;
508 		ndmpd_audit_connect(connection, EINVAL);
509 	}
510 
511 	if (reply.error == NDMP_NO_ERR) {
512 		ndmp_set_authorized(connection, TRUE);
513 	} else {
514 		ndmp_set_authorized(connection, FALSE);
515 		if (tcp_get_peer(connection->conn_sock, &addr.s_addr,
516 		    NULL) != -1) {
517 			NDMP_LOG(LOG_ERR,
518 			    "Authorization(%s) denied for %s.", type,
519 			    inet_ntoa(IN_ADDR(addr)));
520 		}
521 	}
522 
523 	ndmp_send_reply(connection, (void *) &reply,
524 	    "sending ndmp_connect_auth reply");
525 }
526 
527 
528 /*
529  * ndmpd_connect_close_v3
530  *
531  * Close the connection to the DMA.
532  * Send the SHUTDOWN message before closing the socket connection to the DMA.
533  *
534  * Parameters:
535  *   connection (input) - connection handle.
536  *   msginfo    (input) - request message.
537  *
538  * Returns:
539  *   void
540  */
541 /*ARGSUSED*/
542 void
543 ndmpd_connect_close_v3(ndmp_connection_t *connection, void *body)
544 {
545 	ndmpd_session_t *session;
546 	ndmp_lbr_params_t *nlp;
547 	ndmp_notify_connected_request req;
548 
549 	if (!(session = (ndmpd_session_t *)ndmp_get_client_data(connection)))
550 		return;
551 	if ((nlp = ndmp_get_nlp(session)) == NULL)
552 		return;
553 
554 	NDMP_LOG(LOG_DEBUG, "ver: %u",
555 	    session->ns_protocol_version);
556 
557 	/* Send the SHUTDOWN message before closing the connection. */
558 	req.reason = NDMP_SHUTDOWN;
559 	req.protocol_version = session->ns_protocol_version;
560 	req.text_reason = "Connection closed by server.";
561 
562 	if (ndmp_send_request(connection, NDMP_NOTIFY_CONNECTION_STATUS,
563 	    NDMP_NO_ERR, (void *) &req, 0) < 0) {
564 		NDMP_LOG(LOG_NOTICE, "Sending connection shutdown notify");
565 		return;
566 	}
567 
568 	(void) mutex_lock(&nlp->nlp_mtx);
569 	ndmp_close(connection);
570 	session->ns_eof = TRUE;
571 	(void) cond_broadcast(&nlp->nlp_cv);
572 	(void) mutex_unlock(&nlp->nlp_mtx);
573 }
574 
575 /*
576  * ************************************************************************
577  * NDMP V4 HANDLERS
578  * ************************************************************************
579  */
580 
581 /*
582  * ************************************************************************
583  * LOCALS
584  * ************************************************************************
585  */
586 
587 /*
588  * create_md5_digest
589  *
590  * This function uses the MD5 message-digest algorithm described
591  * in RFC1321 to authenticate the client using a shared secret (password).
592  * The message used to compute the MD5 digest is a concatenation of password,
593  * null padding, the 64 byte fixed length challenge and a repeat of the
594  * password. The length of the null padding is chosen to result in a 128 byte
595  * fixed length message. The lengh of the padding can be computed as
596  * 64 - 2*(length of the password). The client digest is computed using the
597  * server challenge from the NDMP_CONFIG_GET_AUTH_ATTR reply.
598  *
599  * Parameters:
600  *   digest (output) - 16 bytes MD5 digest
601  *   passwd (input) - user password
602  *   challenge (input) - 64 bytes server challenge
603  *
604  * Returns:
605  *   void
606  */
607 static void
608 create_md5_digest(unsigned char *digest, char *passwd, unsigned char *challenge)
609 {
610 	char buf[130];
611 	char *p = &buf[0];
612 	int len, i;
613 	MD5_CTX md;
614 	char *pwd;
615 
616 	*p = 0;
617 	pwd = passwd;
618 	if ((len = strlen(pwd)) > MD5_PASS_LIMIT)
619 		len = MD5_PASS_LIMIT;
620 	(void) memcpy(p, pwd, len);
621 	p += len;
622 
623 	for (i = 0; i < MD5_CHALLENGE_SIZE - 2 * len; i++)
624 		*p++ = 0;
625 
626 	(void) memcpy(p, challenge, MD5_CHALLENGE_SIZE);
627 	p += MD5_CHALLENGE_SIZE;
628 	(void) strlcpy(p, pwd, MD5_PASS_LIMIT);
629 
630 	MD5Init(&md);
631 	MD5Update(&md, buf, 128);
632 	MD5Final(digest, &md);
633 }
634 
635 /*
636  * ndmp_connect_list_find
637  *
638  * Find the element in the active connection list.
639  *
640  * Parameters:
641  *   connection (input) - connection handler.
642  *
643  * Returns:
644  *   NULL - error
645  *   connection list element pointer
646  */
647 static struct conn_list *
648 ndmp_connect_list_find(ndmp_connection_t *connection)
649 {
650 	struct conn_list *clp;
651 
652 	NDMP_LOG(LOG_DEBUG, "connection: 0x%p",
653 	    connection);
654 
655 	LIST_FOREACH(clp, &cl_head, cl_q) {
656 		if (clp->cl_conn == connection) {
657 			(void) mutex_unlock(&cl_mutex);
658 			return (clp);
659 		}
660 	}
661 	return (NULL);
662 }
663 
664 /*
665  * ndmpconnect_list_add
666  *
667  * Add the new connection to the list of the active connections.
668  *
669  * Parameters:
670  *   connection (input) - connection handler.
671  *   id (input/output) - pointer to connection id.
672  *
673  * Returns:
674  *   0 - success
675  *  -1 - error
676  */
677 int
678 ndmp_connect_list_add(ndmp_connection_t *connection, int *id)
679 {
680 	struct conn_list *clp;
681 
682 	if (connection == NULL) {
683 		NDMP_LOG(LOG_DEBUG, "Invalid argument");
684 		return (-1);
685 	}
686 
687 	if ((clp = ndmp_malloc(sizeof (struct conn_list))) == NULL)
688 		return (-1);
689 
690 	clp->cl_conn = connection;
691 	clp->cl_id = *id;
692 
693 	(void) mutex_lock(&cl_mutex);
694 	LIST_INSERT_HEAD(&cl_head, clp, cl_q);
695 	(*id)++;
696 	(void) mutex_unlock(&cl_mutex);
697 
698 	return (0);
699 }
700 
701 /*
702  * ndmp_connect_list_del
703  *
704  * Delete the specified connection from the list.
705  *
706  * Parameters:
707  *   connection (input) - connection handler.
708  *
709  * Returns:
710  *   0 - success
711  *  -1 - error
712  */
713 int
714 ndmp_connect_list_del(ndmp_connection_t *connection)
715 {
716 	struct conn_list *clp;
717 
718 	(void) mutex_lock(&cl_mutex);
719 	if (!(clp = ndmp_connect_list_find(connection))) {
720 		(void) mutex_unlock(&cl_mutex);
721 		NDMP_LOG(LOG_DEBUG, "connection not found");
722 		return (-1);
723 	}
724 
725 	LIST_REMOVE(clp, cl_q);
726 	(void) mutex_unlock(&cl_mutex);
727 	free(clp);
728 
729 	return (0);
730 }
731 
732 
733 /*
734  * ndmpconnect_list_find_id
735  *
736  * Find the element specified by its id in the list of active connections.
737  *
738  * Parameters:
739  *   id (input) - connection id.
740  *
741  * Returns:
742  *   NULL - error
743  *   connection list element pointer
744  */
745 static struct conn_list *
746 ndmp_connect_list_find_id(int id)
747 {
748 	struct conn_list *clp;
749 
750 	NDMP_LOG(LOG_DEBUG, "id: %d", id);
751 
752 	(void) mutex_lock(&cl_mutex);
753 	LIST_FOREACH(clp, &cl_head, cl_q) {
754 		if (clp->cl_id == id) {
755 			(void) mutex_unlock(&cl_mutex);
756 			return (clp);
757 		}
758 	}
759 
760 	(void) mutex_unlock(&cl_mutex);
761 	return (NULL);
762 }
763 
764 /*
765  * Get common fields of the active connection.
766  */
767 static void
768 ndmp_connect_get_conn(struct conn_list *clp, ndmp_door_ctx_t *enc_ctx)
769 {
770 	int port;
771 	struct in_addr addr;
772 	char cl_addr[NDMP_CL_ADDR_LEN];
773 	ndmpd_session_t *session;
774 
775 	if (!(session = (ndmpd_session_t *)ndmp_get_client_data(clp->cl_conn)))
776 		return;
777 
778 	ndmp_door_put_int32(enc_ctx, clp->cl_id);
779 	ndmp_door_put_int32(enc_ctx, session->ns_protocol_version);
780 	ndmp_door_put_int32(enc_ctx, clp->cl_conn->conn_authorized);
781 	ndmp_door_put_int32(enc_ctx, session->ns_eof);
782 	if (tcp_get_peer(clp->cl_conn->conn_sock, &(addr.s_addr), &port) != -1)
783 		(void) snprintf(cl_addr, NDMP_CL_ADDR_LEN, "%s:%d",
784 		    (char *)inet_ntoa(addr), port);
785 	else
786 		cl_addr[0] = '\0';
787 	ndmp_door_put_string(enc_ctx, cl_addr);
788 }
789 
790 /*
791  * Get the connection SCSI info.
792  */
793 static void
794 ndmp_connect_get_scsi_v2(ndmpd_session_t *session, ndmp_door_ctx_t *enc_ctx)
795 {
796 	ndmp_door_put_int32(enc_ctx, session->ns_scsi.sd_is_open);
797 	ndmp_door_put_string(enc_ctx, session->ns_scsi.sd_adapter_name);
798 	ndmp_door_put_int32(enc_ctx, session->ns_scsi.sd_valid_target_set);
799 	if (session->ns_scsi.sd_valid_target_set) {
800 		ndmp_door_put_int32(enc_ctx, session->ns_scsi.sd_sid);
801 		ndmp_door_put_int32(enc_ctx, session->ns_scsi.sd_lun);
802 	}
803 }
804 
805 /*
806  * Get the connection tape info.
807  */
808 static void
809 ndmp_connect_get_tape_v2(ndmpd_session_t *session, ndmp_door_ctx_t *enc_ctx)
810 {
811 	char dev_name[NDMP_TAPE_DEV_NAME];
812 
813 	ndmp_door_put_int32(enc_ctx, session->ns_tape.td_fd);
814 	if (session->ns_tape.td_fd != -1) {
815 		ndmp_door_put_uint64(enc_ctx, session->ns_tape.td_record_count);
816 		ndmp_door_put_int32(enc_ctx, session->ns_tape.td_mode);
817 		(void) snprintf(dev_name, NDMP_TAPE_DEV_NAME, "%st%02x%x",
818 		    session->ns_tape.td_adapter_name, session->ns_tape.td_sid,
819 		    session->ns_tape.td_lun);
820 		ndmp_door_put_string(enc_ctx, dev_name);
821 		ndmp_door_put_string(enc_ctx, session->ns_tape.td_adapter_name);
822 		ndmp_door_put_int32(enc_ctx, session->ns_tape.td_sid);
823 		ndmp_door_put_int32(enc_ctx, session->ns_tape.td_lun);
824 	}
825 }
826 
827 /*
828  * Get the connection mover info.
829  */
830 static void
831 ndmp_connect_get_mover_v2(ndmpd_session_t *session, ndmp_door_ctx_t *enc_ctx)
832 {
833 	ndmp_door_put_int32(enc_ctx, session->ns_mover.md_state);
834 	ndmp_door_put_int32(enc_ctx, session->ns_mover.md_mode);
835 	ndmp_door_put_int32(enc_ctx, session->ns_mover.md_pause_reason);
836 	ndmp_door_put_int32(enc_ctx, session->ns_mover.md_halt_reason);
837 	ndmp_door_put_uint64(enc_ctx, session->ns_mover.md_record_size);
838 	ndmp_door_put_uint64(enc_ctx, session->ns_mover.md_record_num);
839 	ndmp_door_put_uint64(enc_ctx, session->ns_mover.md_position);
840 	ndmp_door_put_uint64(enc_ctx, session->ns_mover.md_window_offset);
841 	ndmp_door_put_uint64(enc_ctx, session->ns_mover.md_window_length);
842 	ndmp_door_put_int32(enc_ctx, session->ns_mover.md_sock);
843 }
844 
845 /*
846  * Get the connection common data info.
847  */
848 static void
849 ndmp_connect_get_data_common(ndmpd_session_t *session, ndmp_door_ctx_t *enc_ctx)
850 {
851 	int i;
852 	ndmp_pval *ep;
853 	int len;
854 
855 	ndmp_door_put_int32(enc_ctx, session->ns_data.dd_operation);
856 	ndmp_door_put_int32(enc_ctx, session->ns_data.dd_state);
857 	ndmp_door_put_int32(enc_ctx, session->ns_data.dd_halt_reason);
858 	ndmp_door_put_int32(enc_ctx, session->ns_data.dd_sock);
859 	ndmp_door_put_int32(enc_ctx, session->ns_data.dd_mover.addr_type);
860 	ndmp_door_put_int32(enc_ctx, session->ns_data.dd_abort);
861 	ndmp_door_put_uint64(enc_ctx, session->ns_data.dd_read_offset);
862 	ndmp_door_put_uint64(enc_ctx, session->ns_data.dd_read_length);
863 	ndmp_door_put_uint64(enc_ctx, session->ns_data.dd_data_size);
864 	/* verify data.env has as much data as in session->ns_data.dd_env_len */
865 	len = 0;
866 	ep = session->ns_data.dd_env;
867 	for (i = 0; ep && i < session->ns_data.dd_env_len; i++, ep++)
868 		len++;
869 
870 	/* put the len */
871 	(void) mutex_lock(&session->ns_lock);
872 	ndmp_door_put_uint64(enc_ctx, len);
873 	ep = session->ns_data.dd_env;
874 	for (i = 0; i < len; i++, ep++) {
875 		ndmp_door_put_string(enc_ctx, ep->name);
876 		ndmp_door_put_string(enc_ctx, ep->value);
877 	}
878 	(void) mutex_unlock(&session->ns_lock);
879 }
880 
881 /*
882  * Get the connection data info.
883  */
884 static void
885 ndmp_connect_get_data_v2(ndmpd_session_t *session, ndmp_door_ctx_t *enc_ctx)
886 {
887 	int i;
888 	ndmp_name *np;
889 	char tcp_addr[NDMP_TCP_ADDR_SIZE];
890 
891 	ndmp_connect_get_data_common(session, enc_ctx);
892 
893 	switch (session->ns_data.dd_mover.addr_type) {
894 	case NDMP_ADDR_LOCAL:
895 		(void) snprintf(tcp_addr, NDMP_TCP_ADDR_SIZE, "%s", "Local");
896 		ndmp_door_put_string(enc_ctx, tcp_addr);
897 		break;
898 	case NDMP_ADDR_TCP:
899 		(void) snprintf(tcp_addr, NDMP_TCP_ADDR_SIZE, "%s:%d",
900 		    (char *)inet_ntoa(IN_ADDR(
901 		    session->ns_data.dd_mover.ndmp_mover_addr_u.addr.ip_addr)),
902 		    session->ns_data.dd_mover.ndmp_mover_addr_u.addr.port);
903 		ndmp_door_put_string(enc_ctx, tcp_addr);
904 		break;
905 	default:
906 		(void) snprintf(tcp_addr, NDMP_TCP_ADDR_SIZE, "%s", "Unknown");
907 		ndmp_door_put_string(enc_ctx, tcp_addr);
908 	}
909 
910 	ndmp_door_put_uint64(enc_ctx, session->ns_data.dd_nlist_len);
911 	np = session->ns_data.dd_nlist;
912 	for (i = 0; np && i < (int)session->ns_data.dd_nlist_len; i++, np++) {
913 		ndmp_door_put_string(enc_ctx, np->name);
914 		ndmp_door_put_string(enc_ctx, np->dest);
915 	}
916 }
917 
918 /*
919  * Get V2 connection info.
920  */
921 static void
922 ndmp_connect_get_v2(ndmp_connection_t *connection, ndmp_door_ctx_t *enc_ctx)
923 {
924 	ndmpd_session_t *session;
925 
926 	if ((session = (ndmpd_session_t *)ndmp_get_client_data(connection))) {
927 		ndmp_connect_get_scsi_v2(session, enc_ctx);
928 		ndmp_connect_get_tape_v2(session, enc_ctx);
929 		ndmp_connect_get_mover_v2(session, enc_ctx);
930 		ndmp_connect_get_data_v2(session, enc_ctx);
931 	}
932 }
933 
934 /*
935  * Get the V3 connection mover info.
936  */
937 static void
938 ndmp_connect_get_mover_v3(ndmpd_session_t *session, ndmp_door_ctx_t *enc_ctx)
939 {
940 	char tcp_addr[NDMP_TCP_ADDR_SIZE];
941 
942 	/* get all the V2 mover data first */
943 	ndmp_connect_get_mover_v2(session, enc_ctx);
944 
945 	/* get the V3 mover data now */
946 	ndmp_door_put_int32(enc_ctx, session->ns_mover.md_listen_sock);
947 	ndmp_door_put_int32(enc_ctx, session->ns_mover.md_data_addr.addr_type);
948 	tcp_addr[0] = '\0';
949 	(void) snprintf(tcp_addr, NDMP_TCP_ADDR_SIZE, "%s:%d",
950 	    (char *)
951 	    inet_ntoa(IN_ADDR(session->ns_mover.md_data_addr.tcp_ip_v3)),
952 	    (int)session->ns_mover.md_data_addr.tcp_port_v3);
953 	ndmp_door_put_string(enc_ctx, tcp_addr);
954 }
955 
956 /*
957  * Get the connection data info.
958  */
959 static void
960 ndmp_connect_get_data_v3(ndmpd_session_t *session, ndmp_door_ctx_t *enc_ctx)
961 {
962 	ulong_t i;
963 	mem_ndmp_name_v3_t *np;
964 	char tcp_addr[NDMP_TCP_ADDR_SIZE];
965 
966 	ndmp_connect_get_data_common(session, enc_ctx);
967 
968 	(void) snprintf(tcp_addr, NDMP_TCP_ADDR_SIZE, "%s:%d",
969 	    (char *)inet_ntoa(IN_ADDR(session->ns_data.dd_data_addr.tcp_ip_v3)),
970 	    (int)session->ns_data.dd_data_addr.tcp_port_v3);
971 	ndmp_door_put_string(enc_ctx, tcp_addr);
972 	ndmp_door_put_int32(enc_ctx, session->ns_data.dd_listen_sock);
973 	ndmp_door_put_uint64(enc_ctx,
974 	    session->ns_data.dd_module.dm_stats.ms_bytes_processed);
975 	ndmp_door_put_uint64(enc_ctx, session->ns_data.dd_nlist_len);
976 	np = session->ns_data.dd_nlist_v3;
977 	for (i = 0; np && i < (int)session->ns_data.dd_nlist_len; i++, np++) {
978 		ndmp_door_put_string(enc_ctx, np->nm3_opath);
979 		ndmp_door_put_string(enc_ctx, np->nm3_dpath);
980 		ndmp_door_put_uint64(enc_ctx, np->nm3_node);
981 		ndmp_door_put_uint64(enc_ctx, np->nm3_fh_info);
982 	}
983 }
984 
985 /*
986  * Get V3 connection info.
987  */
988 static void
989 ndmp_connect_get_v3(ndmp_connection_t *connection, ndmp_door_ctx_t *enc_ctx)
990 {
991 	ndmpd_session_t *session;
992 
993 	if ((session = (ndmpd_session_t *)ndmp_get_client_data(connection))) {
994 		ndmp_connect_get_scsi_v2(session, enc_ctx);
995 		ndmp_connect_get_tape_v2(session, enc_ctx);
996 		ndmp_connect_get_mover_v3(session, enc_ctx);
997 		ndmp_connect_get_data_v3(session, enc_ctx);
998 	}
999 }
1000 
1001 /*
1002  * Get the list of all active sessions to the clients.  For each version,
1003  * call the appropriate get function.
1004  */
1005 static void
1006 connection_get(struct conn_list *clp, ndmp_door_ctx_t *enc_ctx)
1007 {
1008 	ndmpd_session_t *session;
1009 
1010 	session = (ndmpd_session_t *)ndmp_get_client_data(clp->cl_conn);
1011 	if (!session) {
1012 		ndmp_door_put_int32(enc_ctx, NDMP_SESSION_NODATA);
1013 		return;
1014 	}
1015 	ndmp_door_put_int32(enc_ctx, NDMP_SESSION_DATA);
1016 
1017 	switch (session->ns_protocol_version) {
1018 	case NDMPV2:
1019 		ndmp_connect_get_conn(clp, enc_ctx);
1020 		ndmp_connect_get_v2(clp->cl_conn, enc_ctx);
1021 		break;
1022 	case NDMPV3:
1023 	case NDMPV4:
1024 		ndmp_connect_get_conn(clp, enc_ctx);
1025 		ndmp_connect_get_v3(clp->cl_conn, enc_ctx);
1026 		break;
1027 	default:
1028 		NDMP_LOG(LOG_DEBUG,
1029 		    "Invalid session (0x%p) version 0x%x", session,
1030 		    session->ns_protocol_version);
1031 	}
1032 }
1033 
1034 /*
1035  * ndmpd_connect_kill
1036  *
1037  * Kill the connection based on its version.
1038  *
1039  * Parameters:
1040  *   connection (input) - connection handler.
1041  *
1042  * Returns:
1043  *   0 - success
1044  *  -1 - error
1045  */
1046 int
1047 ndmpd_connect_kill(ndmp_connection_t *connection)
1048 {
1049 	ndmpd_session_t *session;
1050 
1051 	if (!(session = (ndmpd_session_t *)ndmp_get_client_data(connection)))
1052 		return (-1);
1053 
1054 	switch (session->ns_protocol_version) {
1055 	case NDMPV2:
1056 		ndmpd_connect_close_v2(connection, (void *)NULL);
1057 		break;
1058 	case NDMPV3:
1059 	case NDMPV4:
1060 		ndmpd_connect_close_v3(connection, (void *)NULL);
1061 		break;
1062 	default:
1063 		NDMP_LOG(LOG_DEBUG,
1064 		    "Invalid session (0x%p) version 0x%x", session,
1065 		    session->ns_protocol_version);
1066 	}
1067 
1068 	return (0);
1069 }
1070 
1071 /*
1072  * Get the list of all active sessions to the clients.
1073  */
1074 void
1075 ndmp_connect_list_get(ndmp_door_ctx_t *enc_ctx)
1076 {
1077 	int n;
1078 	struct conn_list *clp;
1079 
1080 	n = 0;
1081 	(void) mutex_lock(&cl_mutex);
1082 	LIST_FOREACH(clp, &cl_head, cl_q) {
1083 		n++;
1084 	}
1085 	/* write number of connections */
1086 	ndmp_door_put_int32(enc_ctx, n);
1087 	n = 0;
1088 	LIST_FOREACH(clp, &cl_head, cl_q) {
1089 		connection_get(clp, enc_ctx);
1090 		n++;
1091 	}
1092 	(void) mutex_unlock(&cl_mutex);
1093 }
1094 
1095 /*
1096  * ndmpd_connect_kill_id
1097  *
1098  * Find a connection by its id and kill it.
1099  *
1100  * Parameters:
1101  *   id (input) - connection id.
1102  *
1103  * Returns:
1104  *   0 - success
1105  *  -1 - error
1106  */
1107 int
1108 ndmpd_connect_kill_id(int id)
1109 {
1110 	struct conn_list *clp;
1111 
1112 	if (!(clp = ndmp_connect_list_find_id(id)))
1113 		return (-1);
1114 
1115 	return (ndmpd_connect_kill(clp->cl_conn));
1116 }
1117 
1118 /* Get the devices info */
1119 void
1120 ndmpd_get_devs(ndmp_door_ctx_t *enc_ctx)
1121 {
1122 	int i, n;
1123 	sasd_drive_t *sd;
1124 	scsi_link_t *slink;
1125 
1126 	if ((n = sasd_dev_count()) == 0) {
1127 		ndmp_door_put_int32(enc_ctx, n);
1128 		NDMP_LOG(LOG_DEBUG, "No device attached.");
1129 		return;
1130 	}
1131 	ndmp_door_put_int32(enc_ctx, n);
1132 
1133 	for (i = 0; i < n; i++) {
1134 		sd = sasd_drive(i);
1135 		slink = sasd_dev_slink(i);
1136 
1137 		ndmp_door_put_int32(enc_ctx, slink->sl_type);
1138 		ndmp_door_put_string(enc_ctx, sd->sd_name);
1139 		ndmp_door_put_int32(enc_ctx, slink->sl_lun);
1140 		ndmp_door_put_int32(enc_ctx, slink->sl_sid);
1141 		ndmp_door_put_string(enc_ctx, sd->sd_vendor);
1142 		ndmp_door_put_string(enc_ctx, sd->sd_id);
1143 		ndmp_door_put_string(enc_ctx, sd->sd_rev);
1144 		ndmp_door_put_string(enc_ctx, sd->sd_serial);
1145 		ndmp_door_put_string(enc_ctx, sd->sd_wwn);
1146 	}
1147 }
1148 
1149 /*
1150  * ndmpd_connect_auth_text
1151  *
1152  * Checks text authorization.
1153  *
1154  * Parameters:
1155  *   auth_id (input) - user name
1156  *   auth_password(input) - password
1157  *
1158  * Returns:
1159  *   NDMP_NO_ERR: on success
1160  *   Other NDMP_ error: invalid user name and password
1161  */
1162 int
1163 ndmpd_connect_auth_text(char *uname, char *auth_id, char *auth_password)
1164 {
1165 	char *passwd, *dec_passwd;
1166 	int rv;
1167 
1168 	if (strcmp(uname, auth_id) != 0) {
1169 		rv = NDMP_NOT_AUTHORIZED_ERR;
1170 	} else {
1171 		passwd = ndmpd_get_prop(NDMP_CLEARTEXT_PASSWORD);
1172 		if (!passwd || !*passwd) {
1173 			rv = NDMP_NOT_AUTHORIZED_ERR;
1174 		} else {
1175 			dec_passwd = ndmp_base64_decode(passwd);
1176 			if (dec_passwd == NULL || *dec_passwd == 0)
1177 				rv = NDMP_NOT_AUTHORIZED_ERR;
1178 			else if (strcmp(auth_password, dec_passwd) != 0)
1179 				rv = NDMP_NOT_AUTHORIZED_ERR;
1180 			else
1181 				rv = NDMP_NO_ERR;
1182 
1183 			free(dec_passwd);
1184 		}
1185 	}
1186 
1187 	if (rv == NDMP_NO_ERR) {
1188 		NDMP_LOG(LOG_DEBUG, "Authorization granted.");
1189 	} else {
1190 		NDMP_LOG(LOG_ERR, "Authorization denied.");
1191 	}
1192 
1193 	return (rv);
1194 }
1195 
1196 
1197 /*
1198  * ndmpd_connect_auth_md5
1199  *
1200  * Checks MD5 authorization.
1201  *
1202  * Parameters:
1203  *   auth_id (input) - user name
1204  *   auth_digest(input) - MD5 digest
1205  * 	This is a 16 bytes digest info which is a MD5 transform of 128 bytes
1206  * 	message (password + padding + server challenge + password). Server
1207  * 	challenge is a 64 bytes random string per NDMP session sent out to the
1208  * 	client on demand (See NDMP_CONFIG_GET_AUTH_ATTR command).
1209  *
1210  * Returns:
1211  *   NDMP_NO_ERR: on success
1212  *   Other NDMP_ error: invalid user name and password
1213  */
1214 int
1215 ndmpd_connect_auth_md5(char *uname, char *auth_id, char *auth_digest,
1216     unsigned char *auth_challenge)
1217 {
1218 	char *passwd, *dec_passwd;
1219 	unsigned char digest[16];
1220 	int rv;
1221 
1222 	if (strcmp(uname, auth_id) != 0) {
1223 		rv = NDMP_NOT_AUTHORIZED_ERR;
1224 	} else {
1225 		passwd = ndmpd_get_prop(NDMP_CRAM_MD5_PASSWORD);
1226 		if (passwd == NULL || *passwd == 0) {
1227 			rv = NDMP_NOT_AUTHORIZED_ERR;
1228 		} else {
1229 			dec_passwd = ndmp_base64_decode(passwd);
1230 
1231 			if (dec_passwd == NULL || *dec_passwd == 0) {
1232 				rv = NDMP_NOT_AUTHORIZED_ERR;
1233 			} else {
1234 				create_md5_digest(digest, dec_passwd,
1235 				    auth_challenge);
1236 				if (memcmp(digest, auth_digest,
1237 				    sizeof (digest)) != 0) {
1238 					rv = NDMP_NOT_AUTHORIZED_ERR;
1239 				} else {
1240 					rv = NDMP_NO_ERR;
1241 				}
1242 			}
1243 			free(dec_passwd);
1244 		}
1245 	}
1246 
1247 	if (rv == NDMP_NO_ERR) {
1248 		NDMP_LOG(LOG_DEBUG, "Authorization granted.");
1249 	} else {
1250 		NDMP_LOG(LOG_ERR, "Authorization denied.");
1251 	}
1252 
1253 	return (rv);
1254 }
1255